From 3da7aea540b1c381303769607a0b11ed5ba98128 Mon Sep 17 00:00:00 2001
From: RuoYi <yzz_ivy@163.com>
Date: Sun, 24 May 2020 20:40:55 +0800
Subject: [PATCH] 若依 1.0
---
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/config/ScheduleConfig.java | 57
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java | 308
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-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/reflect/ReflectUtils.java | 406
ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/domain.java.vm | 85
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/util/CronUtils.java | 63
ruoyi-ui/src/assets/icons/svg/druid.svg | 1
ruoyi-ui/bin/build.bat | 12
ruoyi-ui/src/assets/icons/svg/eye.svg | 1
ruoyi-ui/src/assets/styles/index.scss | 191
ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/controller.java.vm | 114
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/service/ISysJobLogService.java | 56
ruoyi-ui/package.json | 109
ruoyi-ui/src/api/system/dict/type.js | 52
ruoyi-ui/src/assets/icons/svg/system.svg | 2
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/user/UserPasswordNotMatchException.java | 16
ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/enums/OperatorType.java | 24
ruoyi-ui/src/assets/icons/svg/tree.svg | 1
ruoyi-ui/src/assets/icons/svg/bug.svg | 1
ruoyi-ui/src/layout/components/TagsView/ScrollPane.vue | 85
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysDictDataController.java | 111
ruoyi-auth/src/main/resources/banner.txt | 10
ruoyi-ui/src/assets/icons/svg/pdf.svg | 1
ruoyi-ui/src/views/system/dept/index.vue | 317
sql/ry_20200520.sql | 702
ruoyi-ui/build/index.js | 35
ruoyi-ui/src/assets/icons/svg/monitor.svg | 2
ruoyi-ui/.env.development | 8
ruoyi-ui/src/utils/jsencrypt.js | 30
ruoyi-ui/src/assets/icons/svg/job.svg | 1
ruoyi-ui/src/api/system/operlog.js | 26
ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/configure/RedisConfig.java | 43
ruoyi-ui/src/assets/icons/svg/github.svg | 1
ruoyi-ui/src/store/modules/user.js | 96
ruoyi-ui/src/components/Editor/index.vue | 226
ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysOperLog.java | 255
ruoyi-ui/src/store/index.js | 23
ruoyi-ui/src/assets/icons/svg/date.svg | 1
ruoyi-ui/src/views/system/menu/index.vue | 376
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserRoleMapper.xml | 44
pom.xml | 237
ruoyi-ui/src/assets/icons/svg/guide.svg | 1
ruoyi-ui/src/api/system/user.js | 110
ruoyi-ui/src/views/system/log/index.vue | 3
ruoyi-ui/src/views/tool/build/RightPanel.vue | 944 +
ruoyi-ui/src/assets/icons/svg/validCode.svg | 1
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/domain/AjaxResult.java | 148
ruoyi-modules/ruoyi-gen/src/main/resources/vm/vue/index.vue.vm | 442
ruoyi-ui/src/assets/404_images/404.png | 0
ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/serviceImpl.java.vm | 109
ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/util/VelocityInitializer.java | 35
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysPermissionService.java | 22
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleDept.java | 46
ruoyi-ui/src/assets/icons/svg/lock.svg | 1
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysPostController.java | 132
ruoyi-ui/src/assets/image/profile.jpg | 0
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysDictData.java | 176
ruoyi-ui/src/utils/generator/drawingDefalut.js | 29
ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/factory/RemoteLogFallbackFactory.java | 34
ruoyi-ui/src/api/menu.js | 9
ruoyi-ui/src/components/Screenfull/index.vue | 60
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/RouterVo.java | 133
ruoyi-auth/src/main/resources/bootstrap.yml | 24
ruoyi-ui/src/assets/icons/svg/user.svg | 1
sql/quartz.sql | 170
ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/service.java.vm | 61
ruoyi-ui/src/assets/icons/svg/tree-table.svg | 1
ruoyi-common/ruoyi-common-redis/src/main/resources/META-INF/spring.factories | 4
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleDeptMapper.java | 45
ruoyi-ui/src/assets/icons/svg/cascader.svg | 1
ruoyi-ui/src/views/components/icons/element-icons.js | 3
ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/RemoteLogService.java | 28
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/enums/UserStatus.java | 30
ruoyi-ui/src/views/tool/gen/genInfoForm.vue | 154
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/domain/SysJobLog.java | 155
ruoyi-ui/src/assets/icons/svg/checkbox.svg | 1
ruoyi-ui/src/assets/icons/svg/form.svg | 1
ruoyi-ui/src/assets/icons/svg/edit.svg | 1
ruoyi-ui/src/assets/image/login-background.jpg | 0
ruoyi-ui/src/assets/styles/element-variables.scss | 31
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/poi/ExcelUtil.java | 843 +
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/controller/SysJobLogController.java | 92
bin/package.bat | 12
ruoyi-modules/ruoyi-system/pom.xml | 99
ruoyi-auth/src/main/java/com/ruoyi/auth/config/WebSecurityConfig.java | 59
ruoyi-common/ruoyi-common-security/src/main/resources/META-INF/spring.factories | 6
ruoyi-ui/src/views/system/user/profile/userInfo.vue | 80
ruoyi-ui/src/store/modules/tagsView.js | 159
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java | 65
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserPostMapper.xml | 34
ruoyi-ui/src/layout/components/Sidebar/index.vue | 54
ruoyi-common/ruoyi-common-swagger/src/main/java/com/ruoyi/common/swagger/config/SwaggerProperties.java | 343
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysLogininforMapper.java | 42
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/handler/CustomAccessDeniedHandler.java | 33
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/util/QuartzJobExecution.java | 20
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/CacheConstants.java | 19
ruoyi-ui/src/assets/icons/svg/drag.svg | 1
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/BaseException.java | 79
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysLogininforMapper.xml | 57
ruoyi-ui/bin/package.bat | 12
ruoyi-ui/src/layout/components/Navbar.vue | 187
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/service/SysJobLogServiceImpl.java | 86
ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/RouterFunctionConfiguration.java | 38
ruoyi-ui/src/components/RightPanel/index.vue | 149
ruoyi-ui/src/layout/components/Sidebar/Logo.vue | 84
ruoyi-ui/src/utils/generator/html.js | 338
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/page/TableDataInfo.java | 85
ruoyi-ui/src/api/system/menu.js | 60
ruoyi-ui/src/views/monitor/online/index.vue | 128
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/bean/BeanUtils.java | 110
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java | 77
ruoyi-ui/src/assets/icons/svg/theme.svg | 1
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/CustomException.java | 43
ruoyi-modules/ruoyi-gen/src/main/resources/vm/vue/index-tree.vue.vm | 411
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/util/JobInvokeUtil.java | 182
ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/service/GenTableColumnServiceImpl.java | 68
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/util/QuartzDisallowConcurrentExecution.java | 22
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/HttpStatus.java | 89
ruoyi-ui/src/assets/icons/svg/radio.svg | 1
ruoyi-ui/src/assets/icons/svgo.yml | 22
ruoyi-ui/src/store/modules/permission.js | 54
ruoyi-ui/src/views/tool/build/CodeTypeDialog.vue | 106
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ip/IpUtils.java | 189
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java | 76
ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/mapper/GenTableColumnMapper.java | 52
ruoyi-ui/.gitignore | 23
ruoyi-ui/src/assets/icons/svg/input.svg | 1
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysUserController.java | 238
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysPost.java | 123
ruoyi-common/ruoyi-common-datascope/src/main/java/com/ruoyi/common/datascope/annotation/DataScope.java | 28
ruoyi-ui/src/components/Hamburger/index.vue | 44
ruoyi-visual/ruoyi-monitor/pom.xml | 74
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java | 457
ruoyi-ui/src/assets/icons/svg/eye-open.svg | 1
ruoyi-modules/ruoyi-job/src/main/resources/banner.txt | 10
ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/SwaggerProvider.java | 61
ruoyi-ui/src/assets/styles/btn.scss | 99
ruoyi-gateway/src/main/java/com/ruoyi/gateway/service/impl/ValidateCodeServiceImpl.java | 93
ruoyi-ui/src/assets/icons/svg/documentation.svg | 1
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/CaptchaException.java | 16
ruoyi-ui/src/assets/icons/svg/code.svg | 1
ruoyi-modules/ruoyi-gen/src/main/resources/vm/js/api.js.vm | 53
ruoyi-ui/src/assets/icons/index.js | 9
ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/enums/BusinessType.java | 59
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/text/Convert.java | 999 +
ruoyi-modules/ruoyi-gen/src/main/resources/vm/sql/sql.vm | 22
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml | 185
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java | 113
ruoyi-modules/ruoyi-system/src/main/resources/bootstrap.yml | 24
ruoyi-ui/src/api/tool/gen.js | 59
ruoyi-ui/src/components/RuoYi/Doc/index.vue | 21
ruoyi-ui/src/views/tool/build/index.vue | 789 +
ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/HystrixFallbackHandler.java | 36
ruoyi-ui/src/components/RuoYi/Git/index.vue | 21
ruoyi-ui/src/views/tool/build/TreeNodeDialog.vue | 149
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/annotation/EnableCustomConfig.java | 26
ruoyi-ui/src/assets/icons/svg/phone.svg | 1
ruoyi-ui/src/layout/components/Settings/index.vue | 108
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysLogininforController.java | 70
ruoyi-ui/.editorconfig | 22
ruoyi-ui/src/store/getters.js | 15
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/MetaVo.java | 49
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOperLogService.java | 49
ruoyi-ui/src/utils/errorCode.js | 6
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/feign/OAuth2FeignConfig.java | 20
ruoyi-modules/ruoyi-job/src/main/resources/mapper/job/SysJobLogMapper.xml | 93
ruoyi-ui/src/assets/icons/svg/qq.svg | 1
ruoyi-ui/src/views/system/user/profile/userAvatar.vue | 138
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/util/AbstractQuartzJob.java | 106
ruoyi-auth/src/main/java/com/ruoyi/auth/RuoYiAuthApplication.java | 30
ruoyi-ui/src/assets/icons/svg/logininfor.svg | 1
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMenuMapper.java | 37
ruoyi-ui/src/directive/permission/index.js | 15
ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/RuoYiGenApplication.java | 34
ruoyi-ui/src/assets/logo/logo.png | 0
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysDeptController.java | 165
ruoyi-ui/src/utils/request.js | 102
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/util/ScheduleUtils.java | 113
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml | 34
ruoyi-ui/src/components/SvgIcon/index.vue | 61
ruoyi-api/pom.xml | 22
ruoyi-ui/src/assets/icons/svg/row.svg | 1
ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/CaptchaConfig.java | 57
ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/KaptchaTextCreator.java | 75
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/RuoYiSystemApplication.java | 34
ruoyi-ui/src/store/modules/app.js | 56
ruoyi-ui/src/views/dashboard/RaddarChart.vue | 116
ruoyi-ui/src/views/system/user/index.vue | 658
ruoyi-ui/src/assets/icons/svg/online.svg | 1
ruoyi-ui/src/views/tool/build/IconsDialog.vue | 123
ruoyi-ui/src/views/components/icons/index.vue | 87
ruoyi-ui/src/assets/icons/svg/component.svg | 1
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/IdUtils.java | 51
ruoyi-ui/src/assets/icons/svg/clipboard.svg | 1
ruoyi-ui/src/views/tool/gen/editTable.vue | 217
ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/factory/RemoteUserFallbackFactory.java | 34
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml | 27
ruoyi-ui/src/components/IconSelect/requireIcons.js | 11
ruoyi-ui/src/layout/components/Sidebar/SidebarItem.vue | 93
ruoyi-common/ruoyi-common-swagger/pom.xml | 34
ruoyi-ui/src/views/tool/gen/importTable.vue | 117
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml | 145
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysMenuController.java | 150
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysPostServiceImpl.java | 179
ruoyi-ui/vue.config.js | 130
ruoyi-ui/src/assets/icons/svg/server.svg | 1
README.md | 4
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/RuoYiJobApplication.java | 34
ruoyi-ui/src/assets/styles/mixin.scss | 66
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserRole.java | 46
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/DateUtils.java | 155
ruoyi-ui/src/layout/index.vue | 102
ruoyi-ui/src/assets/icons/svg/message.svg | 1
ruoyi-ui/bin/run-web.bat | 12
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml | 155
ruoyi-modules/ruoyi-gen/src/main/resources/bootstrap.yml | 24
ruoyi-ui/src/views/monitor/job/log.vue | 288
ruoyi-ui/src/views/dashboard/PieChart.vue | 79
ruoyi-ui/src/views/system/operlog/index.vue | 308
ruoyi-ui/src/views/error/401.vue | 88
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/page/PageDomain.java | 72
ruoyi-ui/src/store/modules/settings.js | 34
ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/service/IGenTableService.java | 99
ruoyi-ui/README.md | 30
ruoyi-modules/ruoyi-gen/src/main/resources/banner.txt | 10
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-common/ruoyi-common-datascope/src/main/java/com/ruoyi/common/datascope/aspect/DataScopeAspect.java | 162
ruoyi-ui/src/views/system/dict/data.vue | 348
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/page/TableSupport.java | 49
ruoyi-ui/src/settings.js | 31
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/text/UUID.java | 481
ruoyi-ui/public/favicon.ico | 0
ruoyi-ui/src/views/components/icons/svg-icons.js | 10
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/user/CaptchaExpireException.java | 16
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/config/CommonUserConverter.java | 75
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/task/RyTask.java | 28
ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/mapper/GenTableMapper.java | 76
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserRoleMapper.java | 64
ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/service/RedisService.java | 207
ruoyi-common/ruoyi-common-core/pom.xml | 106
ruoyi-ui/src/assets/styles/element-ui.scss | 84
ruoyi-ui/src/api/system/logininfor.js | 26
ruoyi-ui/src/utils/generator/js.js | 236
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/SecurityConstants.java | 50
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/text/CharsetKit.java | 86
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/user/UserException.java | 18
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserOnline.java | 113
ruoyi-ui/src/utils/generator/render.js | 121
ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/util/VelocityUtils.java | 298
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictTypeMapper.java | 87
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysRoleController.java | 156
ruoyi-ui/src/views/dashboard/mixins/resize.js | 56
ruoyi-ui/src/views/dashboard/LineChart.vue | 135
ruoyi-modules/ruoyi-job/src/main/resources/mapper/job/SysJobMapper.xml | 111
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserPost.java | 46
ruoyi-ui/src/views/system/dict/index.vue | 343
ruoyi-ui/src/assets/icons/svg/build.svg | 1
ruoyi-ui/src/views/system/user/profile/index.vue | 91
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/utils/SecurityUtils.java | 92
ruoyi-modules/ruoyi-gen/src/main/resources/logback.xml | 74
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/Constants.java | 98
ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/domain/GenTableColumn.java | 369
ruoyi-ui/src/api/system/dept.js | 68
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/CheckedException.java | 31
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/SpringUtils.java | 114
ruoyi-ui/src/assets/icons/svg/icon.svg | 1
ruoyi-ui/src/layout/components/TagsView/index.vue | 289
ruoyi-ui/src/assets/icons/svg/time.svg | 1
ruoyi-ui/src/assets/icons/svg/example.svg | 1
ruoyi-ui/src/views/system/post/index.vue | 320
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/service/SysJobServiceImpl.java | 253
ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/service/GenTableServiceImpl.java | 340
ruoyi-ui/src/assets/icons/svg/number.svg | 1
ruoyi-ui/src/assets/styles/sidebar.scss | 209
ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/domain/GenTable.java | 271
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysMenu.java | 232
ruoyi-gateway/src/main/resources/bootstrap.yml | 24
ruoyi-ui/src/api/system/post.js | 44
ruoyi-ui/src/components/IconSelect/index.vue | 69
ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/service/AsyncLogService.java | 28
ruoyi-ui/src/assets/icons/svg/slider.svg | 1
ruoyi-ui/src/utils/generator/icon.json | 1
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysNoticeController.java | 92
ruoyi-ui/src/assets/icons/svg/excel.svg | 1
ruoyi-auth/src/main/java/com/ruoyi/auth/exception/CustomWebResponseExceptionTranslator.java | 21
ruoyi-ui/src/assets/401_images/401.gif | 0
ruoyi-visual/ruoyi-monitor/src/main/java/com/ruoyi/modules/monitor/config/WebSecurityConfigurer.java | 49
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/job/TaskException.java | 34
ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/enums/BusinessStatus.java | 20
ruoyi-gateway/src/main/resources/banner.txt | 10
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/feign/OAuth2FeignRequestInterceptor.java | 35
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java | 143
ruoyi-common/ruoyi-common-redis/pom.xml | 33
sql/ry_config.sql | 205
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/ServiceNameConstants.java | 19
ruoyi-ui/src/components/PanThumb/index.vue | 142
ruoyi-ui/src/utils/index.js | 386
ruoyi-ui/src/assets/icons/svg/link.svg | 1
ruoyi-modules/ruoyi-gen/src/main/resources/vm/xml/mapper.xml.vm | 95
ruoyi-ui/src/assets/icons/svg/email.svg | 1
ruoyi-ui/src/views/system/role/index.vue | 541
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/TreeSelect.java | 77
ruoyi-modules/ruoyi-system/src/main/resources/banner.txt | 10
ruoyi-modules/pom.xml | 24
ruoyi-ui/src/assets/icons/svg/people.svg | 1
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/config/ResourceServerConfig.java | 82
bin/clean.bat | 12
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml | 182
ruoyi-ui/src/api/login.js | 39
ruoyi-ui/src/assets/icons/svg/log.svg | 1
ruoyi-ui/src/assets/icons/svg/nested.svg | 1
ruoyi-common/ruoyi-common-core/src/main/resources/META-INF/spring.factories | 4
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDictDataMapper.xml | 123
ruoyi-ui/src/assets/icons/svg/switch.svg | 1
ruoyi-ui/src/permission.js | 64
ruoyi-ui/src/views/tool/gen/basicInfoForm.vue | 61
ruoyi-visual/ruoyi-monitor/src/main/resources/banner.txt | 10
ruoyi-ui/src/views/monitor/druid/index.vue | 26
ruoyi-ui/src/assets/icons/svg/chart.svg | 1
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictTypeService.java | 84
ruoyi-ui/src/views/login.vue | 206
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/text/StrFormatter.java | 92
ruoyi-ui/src/api/system/config.js | 52
ruoyi-ui/src/views/redirect.vue | 12
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/domain/BaseEntity.java | 156
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOperLogMapper.java | 49
ruoyi-ui/src/views/tool/build/DraggableItem.vue | 100
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/UtilException.java | 26
ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/SwaggerHandler.java | 56
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/sign/Base64.java | 291
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/RedisClientDetailsService.java | 30
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysPermissionServiceImpl.java | 63
ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/aspect/LogAspect.java | 219
ruoyi-visual/pom.xml | 22
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysLogininforService.java | 40
ruoyi-modules/ruoyi-gen/src/main/resources/mapper/generator/GenTableColumnMapper.xml | 120
ruoyi-modules/ruoyi-system/src/main/resources/logback.xml | 74
ruoyi-common/ruoyi-common-datascope/pom.xml | 27
ruoyi-ui/src/views/error/404.vue | 233
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysPostService.java | 101
ruoyi-modules/ruoyi-gen/pom.xml | 99
ruoyi-ui/.env.production | 5
ruoyi-ui/.env.staging | 7
ruoyi-common/ruoyi-common-log/src/main/resources/META-INF/spring.factories | 3
ruoyi-ui/src/main.js | 72
ruoyi-ui/src/assets/404_images/404_cloud.png | 0
ruoyi-ui/src/assets/icons/svg/select.svg | 1
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysPostMapper.java | 100
ruoyi-ui/src/router/index.js | 127
ruoyi-ui/src/assets/icons/svg/education.svg | 1
ruoyi-ui/src/assets/styles/transition.scss | 48
ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/service/IGenTableColumnService.java | 44
ruoyi-ui/public/index.html | 207
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/config/AuthIgnoreConfig.java | 28
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysDictTypeController.java | 121
ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysRole.java | 197
ruoyi-common/ruoyi-common-datascope/src/main/resources/META-INF/spring.factories | 5
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysProfileController.java | 81
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/R.java | 99
ruoyi-modules/ruoyi-gen/src/main/resources/mapper/generator/GenTableMapper.xml | 169
ruoyi-api/ruoyi-api-system/pom.xml | 28
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/PermissionService.java | 167
ruoyi-ui/src/assets/icons/svg/table.svg | 1
.gitignore | 43
ruoyi-ui/src/assets/icons/svg/date-range.svg | 1
ruoyi-ui/src/layout/mixin/ResizeHandler.js | 45
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/service/ISysJobService.java | 102
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysMenuService.java | 137
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml | 122
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/annotation/Excels.java | 18
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-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ServletUtils.java | 136
ruoyi-auth/src/main/java/com/ruoyi/auth/controller/UserController.java | 21
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/controller/SysJobController.java | 132
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/DemoModeException.java | 15
ruoyi-ui/src/assets/icons/svg/money.svg | 1
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysOperLogMapper.xml | 83
ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/config/GenConfig.java | 66
ruoyi-ui/src/api/monitor/job.js | 71
ruoyi-ui/src/components/Breadcrumb/index.vue | 81
ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/mapper.java.vm | 61
ruoyi-ui/src/assets/styles/variables.scss | 35
ruoyi-common/ruoyi-common-swagger/src/main/resources/META-INF/spring.factories | 2
ruoyi-ui/src/assets/icons/svg/skill.svg | 1
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/annotation/EnableRyFeignClients.java | 27
ruoyi-modules/ruoyi-job/src/main/resources/bootstrap.yml | 24
ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/model/UserInfo.java | 60
ruoyi-ui/src/assets/icons/svg/tab.svg | 1
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysNoticeMapper.java | 61
ruoyi-ui/src/assets/icons/svg/password.svg | 1
ruoyi-ui/src/assets/icons/svg/wechat.svg | 1
ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/configure/FastJson2JsonRedisSerializer.java | 69
ruoyi-ui/src/assets/icons/svg/dashboard.svg | 1
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java | 168
ruoyi-ui/src/assets/icons/svg/size.svg | 1
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeServiceImpl.java | 93
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/UserConstants.java | 42
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserPostMapper.java | 45
ruoyi-ui/src/directive/permission/hasRole.js | 28
ruoyi-ui/src/assets/icons/svg/shopping.svg | 1
ruoyi-ui/src/utils/ruoyi.js | 147
ruoyi-ui/src/utils/permission.js | 49
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java | 383
ruoyi-auth/src/main/java/com/ruoyi/auth/controller/TokenController.java | 50
ruoyi-auth/src/main/java/com/ruoyi/auth/config/AuthServerConfig.java | 139
ruoyi-ui/src/assets/icons/svg/dict.svg | 1
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java | 131
ruoyi-ui/src/utils/auth.js | 15
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/controller/BaseController.java | 87
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMapper.java | 108
ruoyi-ui/src/components/Pagination/index.vue | 101
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/domain/LoginUser.java | 37
ruoyi-ui/src/views/tool/gen/index.vue | 291
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictDataServiceImpl.java | 118
ruoyi-ui/src/views/dashboard/BarChart.vue | 102
ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysUser.java | 326
ruoyi-ui/src/api/system/notice.js | 44
ruoyi-ui/src/views/index.vue | 98
ruoyi-visual/ruoyi-monitor/src/main/java/com/ruoyi/modules/monitor/RuoYiMonitorApplication.java | 30
ruoyi-ui/src/assets/icons/svg/zip.svg | 1
ruoyi-common/ruoyi-common-swagger/src/main/java/com/ruoyi/common/swagger/annotation/EnableCustomSwagger2.java | 20
ruoyi-ui/src/assets/icons/svg/404.svg | 1
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysNoticeService.java | 61
ruoyi-ui/src/assets/icons/svg/exit-fullscreen.svg | 1
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/UserDetailsServiceImpl.java | 83
ruoyi-ui/src/utils/validate.js | 83
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserOnlineService.java | 47
ruoyi-ui/src/assets/icons/svg/color.svg | 1
ruoyi-ui/src/assets/styles/ruoyi.scss | 197
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/domain/SysJob.java | 172
ruoyi-ui/src/utils/generator/css.js | 18
ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/annotation/Log.java | 41
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysDictType.java | 94
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserOnlineServiceImpl.java | 95
ruoyi-common/ruoyi-common-datascope/src/main/java/com/ruoyi/common/datascope/service/AwaitUserService.java | 42
ruoyi-ui/src/assets/icons/svg/textarea.svg | 1
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java | 312
ruoyi-gateway/pom.xml | 85
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysConfigController.java | 123
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysNotice.java | 101
ruoyi-ui/src/api/monitor/jobLog.js | 26
ruoyi-ui/src/layout/components/Sidebar/Item.vue | 29
ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysDept.java | 202
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/ScheduleConstants.java | 50
ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/ValidateCodeHandler.java | 41
ruoyi-ui/src/assets/icons/svg/time-range.svg | 1
ruoyi-ui/src/directive/permission/hasPermi.js | 28
ruoyi-modules/ruoyi-job/pom.xml | 105
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysLogininfor.java | 144
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/StringUtils.java | 404
ruoyi-gateway/src/main/java/com/ruoyi/gateway/service/ValidateCodeService.java | 23
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysConfig.java | 111
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java | 118
ruoyi-common/ruoyi-common-swagger/src/main/java/com/ruoyi/common/swagger/config/SwaggerAutoConfiguration.java | 132
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/annotation/Excel.java | 113
ruoyi-common/ruoyi-common-security/pom.xml | 33
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleMenu.java | 46
ruoyi-ui/src/views/system/logininfor/index.vue | 220
ruoyi-gateway/src/main/java/com/ruoyi/gateway/RuoYiGatewayApplication.java | 31
ruoyi-ui/src/assets/icons/svg/fullscreen.svg | 1
ruoyi-common/ruoyi-common-log/pom.xml | 33
ruoyi-ui/src/assets/icons/svg/post.svg | 1
ruoyi-ui/src/assets/icons/svg/swagger.svg | 1
ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/RemoteUserService.java | 28
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/GenConstants.java | 94
ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/controller/GenController.java | 186
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysOperlogController.java | 76
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/mapper/SysJobLogMapper.java | 64
ruoyi-ui/src/utils/generator/config.js | 423
ruoyi-ui/src/views/system/notice/index.vue | 345
ruoyi-auth/pom.xml | 79
ruoyi-ui/.eslintrc.js | 199
ruoyi-ui/src/views/monitor/job/index.vue | 481
ruoyi-ui/src/assets/icons/svg/rate.svg | 1
ruoyi-ui/src/views/system/config/index.vue | 339
ruoyi-ui/src/assets/icons/svg/peoples.svg | 1
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictDataService.java | 78
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/mapper/SysJobMapper.java | 67
ruoyi-ui/src/api/system/dict/data.js | 52
ruoyi-ui/src/utils/zipdownload.js | 40
ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/util/GenUtils.java | 242
ruoyi-ui/src/api/monitor/online.js | 18
ruoyi-ui/src/assets/icons/svg/search.svg | 1
ruoyi-ui/src/assets/icons/svg/language.svg | 1
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysConfigMapper.xml | 112
ruoyi-modules/ruoyi-job/src/main/resources/logback.xml | 74
ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/ValidateCodeFilter.java | 53
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ReUtil.java | 164
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDictTypeMapper.xml | 105
ruoyi-ui/src/assets/icons/svg/upload.svg | 1
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDeptMapper.java | 119
ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/properties/IgnoreClientProperties.java | 33
ruoyi-ui/babel.config.js | 5
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ExceptionUtil.java | 40
ruoyi-ui/src/assets/icons/svg/download.svg | 1
ruoyi-ui/src/assets/icons/svg/list.svg | 1
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysRoleService.java | 132
ruoyi-ui/src/assets/icons/svg/question.svg | 1
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
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java | 110
ruoyi-api/ruoyi-api-system/src/main/resources/META-INF/spring.factories | 3
ruoyi-common/pom.xml | 27
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysConfigMapper.java | 69
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/sql/SqlUtil.java | 36
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysNoticeMapper.xml | 89
ruoyi-visual/ruoyi-monitor/src/main/resources/bootstrap.yml | 24
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictDataMapper.java | 97
519 files changed, 46,600 insertions(+), 3 deletions(-)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..4916f7a
--- /dev/null
+++ b/.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/README.md b/README.md
index 5874759..6baab17 100644
--- a/README.md
+++ b/README.md
@@ -2,9 +2,7 @@
* 采用前后端分离的模式,微服务版本前端(基于 [RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue))。
* 后端采用Spring Boot、Spring Cloud & Alibaba、OAuth2。
-* 注册中心、配置中心选型Nacos,为工程瘦身的同时加强各模块之间的联动。
-* 权限认证使用OAuth2,实现了多终端认证系统,可控制业务模块权限。
-* 项目分包明确,规范微服务的开发模式,使包与包之间的分工清晰。
+* 注册中心、配置中心选型Nacos,为权限认证使用OAuth2。
* 如需不分离应用,请移步 [RuoYi](https://gitee.com/y_project/RuoYi),如需分离应用,请移步 [RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue)
* 阿里云优惠券:[点我进入](https://www.aliyun.com/minisite/goods?userCode=brki8iof&share_source=copy_link),腾讯云优惠券:[点我领取](https://cloud.tencent.com/redirect.php?redirect=1025&cps_key=198c8df2ed259157187173bc7f4f32fd&from=console)
diff --git a/bin/clean.bat b/bin/clean.bat
new file mode 100644
index 0000000..e144025
--- /dev/null
+++ b/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/bin/package.bat b/bin/package.bat
new file mode 100644
index 0000000..af8af8e
--- /dev/null
+++ b/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/pom.xml b/pom.xml
new file mode 100644
index 0000000..71d6b12
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,237 @@
+<?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.0</version>
+
+ <name>ruoyi</name>
+ <url>http://www.ruoyi.vip</url>
+ <description>若依微服务系统</description>
+
+ <properties>
+ <ruoyi.version>1.0.0</ruoyi.version>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+ <java.version>1.8</java.version>
+ <spring-boot.version>2.2.6.RELEASE</spring-boot.version>
+ <spring-cloud.version>Hoxton.SR4</spring-cloud.version>
+ <spring-boot-admin.version>2.2.3</spring-boot-admin.version>
+ <spring-boot.mybatis>2.1.2</spring-boot.mybatis>
+ <swagger.fox.version>2.9.2</swagger.fox.version>
+ <swagger.core.version>1.5.24</swagger.core.version>
+ <kaptcha.version>2.3.2</kaptcha.version>
+ <pagehelper.boot.version>1.2.12</pagehelper.boot.version>
+ <commons.io.version>2.5</commons.io.version>
+ <commons.fileupload.version>1.3.3</commons.fileupload.version>
+ <velocity.version>1.7</velocity.version>
+ <fastjson.version>1.2.68</fastjson.version>
+ <poi.version>3.17</poi.version>
+ <common-pool.version>2.6.2</common-pool.version>
+ <bitwalker.version>1.19</bitwalker.version>
+ </properties>
+
+ <!-- 依赖声明 -->
+ <dependencyManagement>
+ <dependencies>
+
+ <!-- SpringCloud 微服务 -->
+ <dependency>
+ <groupId>org.springframework.cloud</groupId>
+ <artifactId>spring-cloud-dependencies</artifactId>
+ <version>${spring-cloud.version}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+
+ <!-- SpringCloud Alibaba 微服务 -->
+ <dependency>
+ <groupId>com.alibaba.cloud</groupId>
+ <artifactId>spring-cloud-alibaba-dependencies</artifactId>
+ <version>2.1.0.RELEASE</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+
+ <!-- SpringBoot 依赖配置 -->
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-dependencies</artifactId>
+ <version>${spring-boot.version}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+
+ <!-- SpringBoot 监控客户端 -->
+ <dependency>
+ <groupId>de.codecentric</groupId>
+ <artifactId>spring-boot-admin-starter-client</artifactId>
+ <version>${spring-boot-admin.version}</version>
+ </dependency>
+
+ <!-- Mybatis 依赖配置 -->
+ <dependency>
+ <groupId>org.mybatis.spring.boot</groupId>
+ <artifactId>mybatis-spring-boot-starter</artifactId>
+ <version>${spring-boot.mybatis}</version>
+ </dependency>
+
+ <!-- Swagger 依赖配置 -->
+ <dependency>
+ <groupId>io.swagger</groupId>
+ <artifactId>swagger-models</artifactId>
+ <version>${swagger.core.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>io.swagger</groupId>
+ <artifactId>swagger-annotations</artifactId>
+ <version>${swagger.core.version}</version>
+ </dependency>
+
+ <!-- 验证码 -->
+ <dependency>
+ <groupId>com.github.penggle</groupId>
+ <artifactId>kaptcha</artifactId>
+ <version>${kaptcha.version}</version>
+ </dependency>
+
+ <!-- pagehelper 分页插件 -->
+ <dependency>
+ <groupId>com.github.pagehelper</groupId>
+ <artifactId>pagehelper-spring-boot-starter</artifactId>
+ <version>${pagehelper.boot.version}</version>
+ </dependency>
+
+ <!-- io常用工具类 -->
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ <version>${commons.io.version}</version>
+ </dependency>
+
+ <!-- excel工具 -->
+ <dependency>
+ <groupId>org.apache.poi</groupId>
+ <artifactId>poi-ooxml</artifactId>
+ <version>${poi.version}</version>
+ </dependency>
+
+ <!-- 文件上传工具类 -->
+ <dependency>
+ <groupId>commons-fileupload</groupId>
+ <artifactId>commons-fileupload</artifactId>
+ <version>${commons.fileupload.version}</version>
+ </dependency>
+
+ <!-- 代码生成使用模板 -->
+ <dependency>
+ <groupId>org.apache.velocity</groupId>
+ <artifactId>velocity</artifactId>
+ <version>${velocity.version}</version>
+ </dependency>
+
+ <!-- JSON 解析器和生成器 -->
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>fastjson</artifactId>
+ <version>${fastjson.version}</version>
+ </dependency>
+
+ <!-- 公共资源池 -->
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-pool2</artifactId>
+ <version>${common-pool.version}</version>
+ </dependency>
+
+ <!-- 解析客户端操作系统、浏览器等 -->
+ <dependency>
+ <groupId>eu.bitwalker</groupId>
+ <artifactId>UserAgentUtils</artifactId>
+ <version>${bitwalker.version}</version>
+ </dependency>
+
+ <!-- 核心模块 -->
+ <dependency>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-common-core</artifactId>
+ <version>${ruoyi.version}</version>
+ </dependency>
+
+ <!-- 接口模块 -->
+ <dependency>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-common-swagger</artifactId>
+ <version>${ruoyi.version}</version>
+ </dependency>
+
+ <!-- 安全模块 -->
+ <dependency>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-common-security</artifactId>
+ <version>${ruoyi.version}</version>
+ </dependency>
+
+ <!-- 权限范围 -->
+ <dependency>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-common-datascope</artifactId>
+ <version>${ruoyi.version}</version>
+ </dependency>
+
+ <!-- 日志记录 -->
+ <dependency>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-common-log</artifactId>
+ <version>${ruoyi.version}</version>
+ </dependency>
+
+ <!-- 缓存服务 -->
+ <dependency>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-common-redis</artifactId>
+ <version>${ruoyi.version}</version>
+ </dependency>
+
+ <!-- 系统接口 -->
+ <dependency>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-api-system</artifactId>
+ <version>${ruoyi.version}</version>
+ </dependency>
+
+ </dependencies>
+ </dependencyManagement>
+
+ <modules>
+ <module>ruoyi-auth</module>
+ <module>ruoyi-gateway</module>
+ <module>ruoyi-visual</module>
+ <module>ruoyi-modules</module>
+ <module>ruoyi-api</module>
+ <module>ruoyi-common</module>
+ </modules>
+ <packaging>pom</packaging>
+
+ <dependencies>
+
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>${java.version}</source>
+ <target>${java.version}</target>
+ <encoding>${project.build.sourceEncoding}</encoding>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
\ No newline at end of file
diff --git a/ruoyi-api/pom.xml b/ruoyi-api/pom.xml
new file mode 100644
index 0000000..7c65578
--- /dev/null
+++ b/ruoyi-api/pom.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi</artifactId>
+ <version>1.0.0</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <modules>
+ <module>ruoyi-api-system</module>
+ </modules>
+
+ <artifactId>ruoyi-api</artifactId>
+ <packaging>pom</packaging>
+
+ <description>
+ ruoyi-api系统接口
+ </description>
+
+</project>
diff --git a/ruoyi-api/ruoyi-api-system/pom.xml b/ruoyi-api/ruoyi-api-system/pom.xml
new file mode 100644
index 0000000..5033318
--- /dev/null
+++ b/ruoyi-api/ruoyi-api-system/pom.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns="http://maven.apache.org/POM/4.0.0"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-api</artifactId>
+ <version>1.0.0</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>ruoyi-api-system</artifactId>
+
+ <description>
+ ruoyi-api-system系统接口模块
+ </description>
+
+ <dependencies>
+
+ <!-- RuoYi Common Core-->
+ <dependency>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-common-core</artifactId>
+ </dependency>
+
+ </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/RemoteLogService.java b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/RemoteLogService.java
new file mode 100644
index 0000000..38345d7
--- /dev/null
+++ b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/RemoteLogService.java
@@ -0,0 +1,28 @@
+package com.ruoyi.system.api;
+
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import com.ruoyi.common.core.constant.ServiceNameConstants;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.system.api.domain.SysOperLog;
+import com.ruoyi.system.api.factory.RemoteLogFallbackFactory;
+
+/**
+ * 日志服务
+ *
+ * @author ruoyi
+ */
+@FeignClient(contextId = "remoteLogService", value = ServiceNameConstants.SYSTEM_SERVICE, fallbackFactory = RemoteLogFallbackFactory.class)
+public interface RemoteLogService
+{
+ /**
+ * 保存系统日志
+ *
+ * @param sysOperLog 日志实体
+ * @param from 内部调用标志
+ * @return 结果
+ */
+ @PostMapping("/operlog")
+ R<Boolean> saveLog(@RequestBody SysOperLog sysOperLog);
+}
diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/RemoteUserService.java b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/RemoteUserService.java
new file mode 100644
index 0000000..2c8eee8
--- /dev/null
+++ b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/RemoteUserService.java
@@ -0,0 +1,28 @@
+package com.ruoyi.system.api;
+
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import com.ruoyi.common.core.constant.ServiceNameConstants;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.system.api.factory.RemoteUserFallbackFactory;
+import com.ruoyi.system.api.model.UserInfo;
+
+/**
+ * 用户服务
+ *
+ * @author ruoyi
+ */
+@FeignClient(contextId = "remoteUserService", value = ServiceNameConstants.SYSTEM_SERVICE, fallbackFactory = RemoteUserFallbackFactory.class)
+public interface RemoteUserService
+{
+ /**
+ * 通过用户名查询用户信息
+ *
+ * @param username 用户名
+ * @param from 调用标志
+ * @return 结果
+ */
+ @GetMapping(value = "/user/info/{username}")
+ public R<UserInfo> getUserInfo(@PathVariable("username") String username);
+}
diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysDept.java b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysDept.java
new file mode 100644
index 0000000..04a4e1b
--- /dev/null
+++ b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysDept.java
@@ -0,0 +1,202 @@
+package com.ruoyi.system.api.domain;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.validation.constraints.Email;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Size;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.ruoyi.common.core.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;
+ }
+
+ @NotBlank(message = "部门名称不能为空")
+ @Size(min = 0, max = 30, message = "部门名称长度不能超过30个字符")
+ public String getDeptName()
+ {
+ return deptName;
+ }
+
+ public void setDeptName(String deptName)
+ {
+ this.deptName = deptName;
+ }
+
+ @NotBlank(message = "显示顺序不能为空")
+ 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;
+ }
+
+ @Size(min = 0, max = 11, message = "联系电话长度不能超过11个字符")
+ public String getPhone()
+ {
+ return phone;
+ }
+
+ public void setPhone(String phone)
+ {
+ this.phone = phone;
+ }
+
+ @Email(message = "邮箱格式不正确")
+ @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符")
+ 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;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+ .append("deptId", getDeptId())
+ .append("parentId", getParentId())
+ .append("ancestors", getAncestors())
+ .append("deptName", getDeptName())
+ .append("orderNum", getOrderNum())
+ .append("leader", getLeader())
+ .append("phone", getPhone())
+ .append("email", getEmail())
+ .append("status", getStatus())
+ .append("delFlag", getDelFlag())
+ .append("createBy", getCreateBy())
+ .append("createTime", getCreateTime())
+ .append("updateBy", getUpdateBy())
+ .append("updateTime", getUpdateTime())
+ .toString();
+ }
+}
diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysOperLog.java b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysOperLog.java
new file mode 100644
index 0000000..10320fe
--- /dev/null
+++ b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysOperLog.java
@@ -0,0 +1,255 @@
+package com.ruoyi.system.api.domain;
+
+import java.util.Date;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.common.core.annotation.Excel;
+import com.ruoyi.common.core.annotation.Excel.ColumnType;
+import com.ruoyi.common.core.web.domain.BaseEntity;
+
+/**
+ * 操作日志记录表 oper_log
+ *
+ * @author ruoyi
+ */
+public class SysOperLog extends BaseEntity
+{
+ private static final long serialVersionUID = 1L;
+
+ /** 日志主键 */
+ @Excel(name = "操作序号", cellType = ColumnType.NUMERIC)
+ private Long operId;
+
+ /** 操作模块 */
+ @Excel(name = "操作模块")
+ private String title;
+
+ /** 业务类型(0其它 1新增 2修改 3删除) */
+ @Excel(name = "业务类型", readConverterExp = "0=其它,1=新增,2=修改,3=删除,4=授权,5=导出,6=导入,7=强退,8=生成代码,9=清空数据")
+ private Integer businessType;
+
+ /** 业务类型数组 */
+ private Integer[] businessTypes;
+
+ /** 请求方法 */
+ @Excel(name = "请求方法")
+ private String method;
+
+ /** 请求方式 */
+ @Excel(name = "请求方式")
+ private String requestMethod;
+
+ /** 操作类别(0其它 1后台用户 2手机端用户) */
+ @Excel(name = "操作类别", readConverterExp = "0=其它,1=后台用户,2=手机端用户")
+ private Integer operatorType;
+
+ /** 操作人员 */
+ @Excel(name = "操作人员")
+ private String operName;
+
+ /** 部门名称 */
+ @Excel(name = "部门名称")
+ private String deptName;
+
+ /** 请求url */
+ @Excel(name = "请求地址")
+ private String operUrl;
+
+ /** 操作地址 */
+ @Excel(name = "操作地址")
+ private String operIp;
+
+ /** 操作地点 */
+ @Excel(name = "操作地点")
+ private String operLocation;
+
+ /** 请求参数 */
+ @Excel(name = "请求参数")
+ private String operParam;
+
+ /** 返回参数 */
+ @Excel(name = "返回参数")
+ private String jsonResult;
+
+ /** 操作状态(0正常 1异常) */
+ @Excel(name = "状态", readConverterExp = "0=正常,1=异常")
+ private Integer status;
+
+ /** 错误消息 */
+ @Excel(name = "错误消息")
+ private String errorMsg;
+
+ /** 操作时间 */
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ @Excel(name = "操作时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+ 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-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysRole.java b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysRole.java
new file mode 100644
index 0000000..c15c9e6
--- /dev/null
+++ b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysRole.java
@@ -0,0 +1,197 @@
+package com.ruoyi.system.api.domain;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Size;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.ruoyi.common.core.annotation.Excel;
+import com.ruoyi.common.core.annotation.Excel.ColumnType;
+import com.ruoyi.common.core.web.domain.BaseEntity;
+
+/**
+ * 角色表 sys_role
+ *
+ * @author ruoyi
+ */
+public class SysRole extends BaseEntity
+{
+ private static final long serialVersionUID = 1L;
+
+ /** 角色ID */
+ @Excel(name = "角色序号", cellType = ColumnType.NUMERIC)
+ private Long roleId;
+
+ /** 角色名称 */
+ @Excel(name = "角色名称")
+ private String roleName;
+
+ /** 角色权限 */
+ @Excel(name = "角色权限")
+ private String roleKey;
+
+ /** 角色排序 */
+ @Excel(name = "角色排序")
+ private String roleSort;
+
+ /** 数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限) */
+ @Excel(name = "数据范围", readConverterExp = "1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限")
+ private String dataScope;
+
+ /** 角色状态(0正常 1停用) */
+ @Excel(name = "角色状态", readConverterExp = "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;
+ }
+
+ @NotBlank(message = "角色名称不能为空")
+ @Size(min = 0, max = 30, message = "角色名称长度不能超过30个字符")
+ public String getRoleName()
+ {
+ return roleName;
+ }
+
+ public void setRoleName(String roleName)
+ {
+ this.roleName = roleName;
+ }
+
+ @NotBlank(message = "权限字符不能为空")
+ @Size(min = 0, max = 100, message = "权限字符长度不能超过100个字符")
+ public String getRoleKey()
+ {
+ return roleKey;
+ }
+
+ public void setRoleKey(String roleKey)
+ {
+ this.roleKey = roleKey;
+ }
+
+ @NotBlank(message = "显示顺序不能为空")
+ 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;
+ }
+
+ public String toString() {
+ return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+ .append("roleId", getRoleId())
+ .append("roleName", getRoleName())
+ .append("roleKey", getRoleKey())
+ .append("roleSort", getRoleSort())
+ .append("dataScope", getDataScope())
+ .append("status", getStatus())
+ .append("delFlag", getDelFlag())
+ .append("createBy", getCreateBy())
+ .append("createTime", getCreateTime())
+ .append("updateBy", getUpdateBy())
+ .append("updateTime", getUpdateTime())
+ .append("remark", getRemark())
+ .toString();
+ }
+}
diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysUser.java b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysUser.java
new file mode 100644
index 0000000..e0b84ce
--- /dev/null
+++ b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysUser.java
@@ -0,0 +1,326 @@
+package com.ruoyi.system.api.domain;
+
+import java.util.Date;
+import java.util.List;
+import javax.validation.constraints.Email;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Size;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.ruoyi.common.core.annotation.Excel;
+import com.ruoyi.common.core.annotation.Excel.ColumnType;
+import com.ruoyi.common.core.annotation.Excel.Type;
+import com.ruoyi.common.core.annotation.Excels;
+import com.ruoyi.common.core.web.domain.BaseEntity;
+
+/**
+ * 用户对象 sys_user
+ *
+ * @author ruoyi
+ */
+public class SysUser extends BaseEntity
+{
+ private static final long serialVersionUID = 1L;
+
+ /** 用户ID */
+ @Excel(name = "用户序号", cellType = ColumnType.NUMERIC, prompt = "用户编号")
+ private Long userId;
+
+ /** 部门ID */
+ @Excel(name = "部门编号", type = Type.IMPORT)
+ private Long deptId;
+
+ /** 用户账号 */
+ @Excel(name = "登录名称")
+ private String userName;
+
+ /** 用户昵称 */
+ @Excel(name = "用户名称")
+ private String nickName;
+
+ /** 用户邮箱 */
+ @Excel(name = "用户邮箱")
+ private String email;
+
+ /** 手机号码 */
+ @Excel(name = "手机号码")
+ private String phonenumber;
+
+ /** 用户性别 */
+ @Excel(name = "用户性别", readConverterExp = "0=男,1=女,2=未知")
+ private String sex;
+
+ /** 用户头像 */
+ private String avatar;
+
+ /** 密码 */
+ private String password;
+
+ /** 盐加密 */
+ private String salt;
+
+ /** 帐号状态(0正常 1停用) */
+ @Excel(name = "帐号状态", readConverterExp = "0=正常,1=停用")
+ private String status;
+
+ /** 删除标志(0代表存在 2代表删除) */
+ private String delFlag;
+
+ /** 最后登陆IP */
+ @Excel(name = "最后登陆IP", type = Type.EXPORT)
+ private String loginIp;
+
+ /** 最后登陆时间 */
+ @Excel(name = "最后登陆时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", type = Type.EXPORT)
+ private Date loginDate;
+
+ /** 部门对象 */
+ @Excels({
+ @Excel(name = "部门名称", targetAttr = "deptName", type = Type.EXPORT),
+ @Excel(name = "部门负责人", targetAttr = "leader", type = Type.EXPORT)
+ })
+ 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;
+ }
+
+ @Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符")
+ public String getNickName()
+ {
+ return nickName;
+ }
+
+ public void setNickName(String nickName)
+ {
+ this.nickName = nickName;
+ }
+
+ @NotBlank(message = "用户账号不能为空")
+ @Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符")
+ public String getUserName()
+ {
+ return userName;
+ }
+
+ public void setUserName(String userName)
+ {
+ this.userName = userName;
+ }
+
+ @Email(message = "邮箱格式不正确")
+ @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符")
+ public String getEmail()
+ {
+ return email;
+ }
+
+ public void setEmail(String email)
+ {
+ this.email = email;
+ }
+
+ @Size(min = 0, max = 11, message = "手机号码长度不能超过11个字符")
+ 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;
+ }
+
+ @JsonProperty
+ 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;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+ .append("userId", getUserId())
+ .append("deptId", getDeptId())
+ .append("userName", getUserName())
+ .append("nickName", getNickName())
+ .append("email", getEmail())
+ .append("phonenumber", getPhonenumber())
+ .append("sex", getSex())
+ .append("avatar", getAvatar())
+ .append("password", getPassword())
+ .append("salt", getSalt())
+ .append("status", getStatus())
+ .append("delFlag", getDelFlag())
+ .append("loginIp", getLoginIp())
+ .append("loginDate", getLoginDate())
+ .append("createBy", getCreateBy())
+ .append("createTime", getCreateTime())
+ .append("updateBy", getUpdateBy())
+ .append("updateTime", getUpdateTime())
+ .append("remark", getRemark())
+ .append("dept", getDept())
+ .toString();
+ }
+}
diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/factory/RemoteLogFallbackFactory.java b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/factory/RemoteLogFallbackFactory.java
new file mode 100644
index 0000000..96f6c33
--- /dev/null
+++ b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/factory/RemoteLogFallbackFactory.java
@@ -0,0 +1,34 @@
+package com.ruoyi.system.api.factory;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.system.api.RemoteLogService;
+import com.ruoyi.system.api.domain.SysOperLog;
+import feign.hystrix.FallbackFactory;
+
+/**
+ * 日志服务降级处理
+ *
+ * @author ruoyi
+ */
+@Component
+public class RemoteLogFallbackFactory implements FallbackFactory<RemoteLogService>
+{
+ private static final Logger log = LoggerFactory.getLogger(RemoteLogFallbackFactory.class);
+
+ @Override
+ public RemoteLogService create(Throwable throwable)
+ {
+ log.error("日志服务调用失败:{}", throwable.getMessage());
+ return new RemoteLogService()
+ {
+ @Override
+ public R<Boolean> saveLog(SysOperLog sysOperLog)
+ {
+ return null;
+ }
+ };
+ }
+}
diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/factory/RemoteUserFallbackFactory.java b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/factory/RemoteUserFallbackFactory.java
new file mode 100644
index 0000000..59041e2
--- /dev/null
+++ b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/factory/RemoteUserFallbackFactory.java
@@ -0,0 +1,34 @@
+package com.ruoyi.system.api.factory;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.system.api.RemoteUserService;
+import com.ruoyi.system.api.model.UserInfo;
+import feign.hystrix.FallbackFactory;
+
+/**
+ * 用户服务降级处理
+ *
+ * @author ruoyi
+ */
+@Component
+public class RemoteUserFallbackFactory implements FallbackFactory<RemoteUserService>
+{
+ private static final Logger log = LoggerFactory.getLogger(RemoteUserFallbackFactory.class);
+
+ @Override
+ public RemoteUserService create(Throwable throwable)
+ {
+ log.error("用户服务调用失败:{}", throwable.getMessage());
+ return new RemoteUserService()
+ {
+ @Override
+ public R<UserInfo> getUserInfo(String username)
+ {
+ return null;
+ }
+ };
+ }
+}
diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/model/UserInfo.java b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/model/UserInfo.java
new file mode 100644
index 0000000..3a7225b
--- /dev/null
+++ b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/model/UserInfo.java
@@ -0,0 +1,60 @@
+package com.ruoyi.system.api.model;
+
+import java.io.Serializable;
+import java.util.Set;
+import com.ruoyi.system.api.domain.SysUser;
+
+/**
+ * 用户信息
+ *
+ * @author ruoyi
+ */
+public class UserInfo implements Serializable
+{
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 用户基本信息
+ */
+ private SysUser sysUser;
+
+ /**
+ * 权限标识集合
+ */
+ private Set<String> permissions;
+
+ /**
+ * 角色集合
+ */
+ private Set<String> roles;
+
+ public SysUser getSysUser()
+ {
+ return sysUser;
+ }
+
+ public void setSysUser(SysUser sysUser)
+ {
+ this.sysUser = sysUser;
+ }
+
+ public Set<String> getPermissions()
+ {
+ return permissions;
+ }
+
+ public void setPermissions(Set<String> permissions)
+ {
+ this.permissions = permissions;
+ }
+
+ public Set<String> getRoles()
+ {
+ return roles;
+ }
+
+ public void setRoles(Set<String> roles)
+ {
+ this.roles = roles;
+ }
+}
diff --git a/ruoyi-api/ruoyi-api-system/src/main/resources/META-INF/spring.factories b/ruoyi-api/ruoyi-api-system/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000..e9bf345
--- /dev/null
+++ b/ruoyi-api/ruoyi-api-system/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,3 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+ com.ruoyi.system.api.factory.RemoteUserFallbackFactory,\
+ com.ruoyi.system.api.factory.RemoteLogFallbackFactory
diff --git a/ruoyi-auth/pom.xml b/ruoyi-auth/pom.xml
new file mode 100644
index 0000000..e1a8c74
--- /dev/null
+++ b/ruoyi-auth/pom.xml
@@ -0,0 +1,79 @@
+<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">
+ <parent>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi</artifactId>
+ <version>1.0.0</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>ruoyi-auth</artifactId>
+
+ <description>
+ ruoyi-auth认证授权中心
+ </description>
+
+ <dependencies>
+
+ <!-- SpringCloud Ailibaba Nacos -->
+ <dependency>
+ <groupId>com.alibaba.cloud</groupId>
+ <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
+ </dependency>
+
+ <!-- SpringCloud Ailibaba Nacos Config -->
+ <dependency>
+ <groupId>com.alibaba.cloud</groupId>
+ <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
+ </dependency>
+
+ <!-- SpringCloud Netflix Hystrix -->
+ <dependency>
+ <groupId>org.springframework.cloud</groupId>
+ <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
+ </dependency>
+
+ <!-- SpringBoot Web -->
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-web</artifactId>
+ </dependency>
+
+ <!-- Mysql Connector -->
+ <dependency>
+ <groupId>mysql</groupId>
+ <artifactId>mysql-connector-java</artifactId>
+ </dependency>
+
+ <!-- RuoYi Common Security-->
+ <dependency>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-common-security</artifactId>
+ </dependency>
+
+ <!-- RuoYi Common Redis-->
+ <dependency>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-common-redis</artifactId>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>repackage</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/ruoyi-auth/src/main/java/com/ruoyi/auth/RuoYiAuthApplication.java b/ruoyi-auth/src/main/java/com/ruoyi/auth/RuoYiAuthApplication.java
new file mode 100644
index 0000000..23c770c
--- /dev/null
+++ b/ruoyi-auth/src/main/java/com/ruoyi/auth/RuoYiAuthApplication.java
@@ -0,0 +1,30 @@
+package com.ruoyi.auth;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.cloud.client.SpringCloudApplication;
+import com.ruoyi.common.security.annotation.EnableRyFeignClients;
+
+/**
+ * 认证授权中心
+ *
+ * @author ruoyi
+ */
+@EnableRyFeignClients
+@SpringCloudApplication
+public class RuoYiAuthApplication
+{
+ public static void main(String[] args)
+ {
+ SpringApplication.run(RuoYiAuthApplication.class, args);
+ System.out.println("(♥◠‿◠)ノ゙ 认证授权中心启动成功 ლ(´ڡ`ლ)゙ \n" +
+ " .-------. ____ __ \n" +
+ " | _ _ \\ \\ \\ / / \n" +
+ " | ( ' ) | \\ _. / ' \n" +
+ " |(_ o _) / _( )_ .' \n" +
+ " | (_,_).' __ ___(_ o _)' \n" +
+ " | |\\ \\ | || |(_,_)' \n" +
+ " | | \\ `' /| `-' / \n" +
+ " | | \\ / \\ / \n" +
+ " ''-' `'-' `-..-' ");
+ }
+}
diff --git a/ruoyi-auth/src/main/java/com/ruoyi/auth/config/AuthServerConfig.java b/ruoyi-auth/src/main/java/com/ruoyi/auth/config/AuthServerConfig.java
new file mode 100644
index 0000000..53bb2cf
--- /dev/null
+++ b/ruoyi-auth/src/main/java/com/ruoyi/auth/config/AuthServerConfig.java
@@ -0,0 +1,139 @@
+package com.ruoyi.auth.config;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import javax.sql.DataSource;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.http.HttpMethod;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
+import org.springframework.security.oauth2.common.OAuth2AccessToken;
+import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
+import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
+import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
+import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
+import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
+import org.springframework.security.oauth2.provider.OAuth2Authentication;
+import org.springframework.security.oauth2.provider.token.TokenEnhancer;
+import org.springframework.security.oauth2.provider.token.TokenStore;
+import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
+import com.ruoyi.auth.exception.CustomWebResponseExceptionTranslator;
+import com.ruoyi.common.core.constant.CacheConstants;
+import com.ruoyi.common.core.constant.SecurityConstants;
+import com.ruoyi.common.security.domain.LoginUser;
+import com.ruoyi.common.security.service.RedisClientDetailsService;
+
+/**
+ * OAuth2 认证服务配置
+ *
+ * @author ruoyi
+ */
+@Configuration
+@EnableAuthorizationServer
+public class AuthServerConfig extends AuthorizationServerConfigurerAdapter
+{
+ @Autowired
+ private AuthenticationManager authenticationManager;
+
+ @Autowired
+ private DataSource dataSource;
+
+ @Autowired
+ private RedisConnectionFactory redisConnectionFactory;
+
+ @Autowired
+ private UserDetailsService userDetailsService;
+
+ @Autowired
+ private TokenEnhancer tokenEnhancer;
+
+ /**
+ * 定义授权和令牌端点以及令牌服务
+ */
+ @Override
+ public void configure(AuthorizationServerEndpointsConfigurer endpoints)
+ {
+ endpoints
+ // 请求方式
+ .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
+ // 指定token存储位置
+ .tokenStore(tokenStore())
+ // 自定义生成令牌
+ .tokenEnhancer(tokenEnhancer)
+ // 用户账号密码认证
+ .userDetailsService(userDetailsService)
+ // 指定认证管理器
+ .authenticationManager(authenticationManager)
+ // 是否重复使用 refresh_token
+ .reuseRefreshTokens(false)
+ // 自定义异常处理
+ .exceptionTranslator(new CustomWebResponseExceptionTranslator());
+ }
+
+ /**
+ * 配置令牌端点(Token Endpoint)的安全约束
+ */
+ @Override
+ public void configure(AuthorizationServerSecurityConfigurer oauthServer)
+ {
+ oauthServer.allowFormAuthenticationForClients().checkTokenAccess("permitAll()");
+ }
+
+ /**
+ * 声明 ClientDetails实现
+ */
+ public RedisClientDetailsService clientDetailsService()
+ {
+ RedisClientDetailsService clientDetailsService = new RedisClientDetailsService(dataSource);
+ return clientDetailsService;
+ }
+
+ /**
+ * 配置客户端详情
+ */
+ @Override
+ public void configure(ClientDetailsServiceConfigurer clients) throws Exception
+ {
+ clients.withClientDetails(clientDetailsService());
+ }
+
+ /**
+ * 基于 Redis 实现,令牌保存到缓存
+ */
+ @Bean
+ public TokenStore tokenStore()
+ {
+ RedisTokenStore tokenStore = new RedisTokenStore(redisConnectionFactory);
+ tokenStore.setPrefix(CacheConstants.OAUTH_ACCESS);
+ return tokenStore;
+ }
+
+ /**
+ * 自定义生成令牌
+ */
+ @Bean
+ public TokenEnhancer tokenEnhancer()
+ {
+ return new TokenEnhancer()
+ {
+ @Override
+ public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication)
+ {
+ if (accessToken instanceof DefaultOAuth2AccessToken)
+ {
+ DefaultOAuth2AccessToken token = (DefaultOAuth2AccessToken) accessToken;
+ LoginUser user = (LoginUser) authentication.getUserAuthentication().getPrincipal();
+ Map<String, Object> additionalInformation = new LinkedHashMap<String, Object>();
+ additionalInformation.put(SecurityConstants.DETAILS_USERNAME, authentication.getName());
+ additionalInformation.put(SecurityConstants.DETAILS_USER_ID, user.getUserId());
+ token.setAdditionalInformation(additionalInformation);
+ }
+ return accessToken;
+ };
+ };
+ }
+}
diff --git a/ruoyi-auth/src/main/java/com/ruoyi/auth/config/WebSecurityConfig.java b/ruoyi-auth/src/main/java/com/ruoyi/auth/config/WebSecurityConfig.java
new file mode 100644
index 0000000..df0bc04
--- /dev/null
+++ b/ruoyi-auth/src/main/java/com/ruoyi/auth/config/WebSecurityConfig.java
@@ -0,0 +1,59 @@
+package com.ruoyi.auth.config;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.annotation.Order;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+/**
+ * Security 安全认证相关配置
+ * Oauth2依赖于Security 默认情况下WebSecurityConfig执行比ResourceServerConfig优先
+ *
+ * @author ruoyi
+ */
+@Order(99)
+@Configuration
+public class WebSecurityConfig extends WebSecurityConfigurerAdapter
+{
+ @Autowired
+ private UserDetailsService userDetailsService;
+
+ @Bean
+ public PasswordEncoder passwordEncoder()
+ {
+ return new BCryptPasswordEncoder();
+ }
+
+ @Bean
+ @Override
+ public AuthenticationManager authenticationManagerBean() throws Exception
+ {
+ return super.authenticationManagerBean();
+ }
+
+ @Override
+ protected void configure(AuthenticationManagerBuilder auth) throws Exception
+ {
+ auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
+ }
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception
+ {
+ http
+ .authorizeRequests()
+ .antMatchers(
+ "/actuator/**",
+ "/oauth/*",
+ "/token/**").permitAll()
+ .anyRequest().authenticated()
+ .and().csrf().disable();
+ }
+}
diff --git a/ruoyi-auth/src/main/java/com/ruoyi/auth/controller/TokenController.java b/ruoyi-auth/src/main/java/com/ruoyi/auth/controller/TokenController.java
new file mode 100644
index 0000000..963e063
--- /dev/null
+++ b/ruoyi-auth/src/main/java/com/ruoyi/auth/controller/TokenController.java
@@ -0,0 +1,50 @@
+package com.ruoyi.auth.controller;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpHeaders;
+import org.springframework.security.oauth2.common.OAuth2AccessToken;
+import org.springframework.security.oauth2.common.OAuth2RefreshToken;
+import org.springframework.security.oauth2.provider.token.TokenStore;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.common.core.utils.StringUtils;
+
+/**
+ * token 控制
+ *
+ * @author ruoyi
+ */
+@RestController
+@RequestMapping("/token")
+public class TokenController
+{
+ @Autowired
+ private TokenStore tokenStore;
+
+ @DeleteMapping("/logout")
+ public R<?> logout(@RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authHeader)
+ {
+ if (StringUtils.isEmpty(authHeader))
+ {
+ return R.ok();
+ }
+
+ String tokenValue = authHeader.replace(OAuth2AccessToken.BEARER_TYPE, StringUtils.EMPTY).trim();
+ OAuth2AccessToken accessToken = tokenStore.readAccessToken(tokenValue);
+ if (accessToken == null || StringUtils.isEmpty(accessToken.getValue()))
+ {
+ return R.ok();
+ }
+
+ // 清空 access token
+ tokenStore.removeAccessToken(accessToken);
+
+ // 清空 refresh token
+ OAuth2RefreshToken refreshToken = accessToken.getRefreshToken();
+ tokenStore.removeRefreshToken(refreshToken);
+ return R.ok();
+ }
+}
diff --git a/ruoyi-auth/src/main/java/com/ruoyi/auth/controller/UserController.java b/ruoyi-auth/src/main/java/com/ruoyi/auth/controller/UserController.java
new file mode 100644
index 0000000..d470584
--- /dev/null
+++ b/ruoyi-auth/src/main/java/com/ruoyi/auth/controller/UserController.java
@@ -0,0 +1,21 @@
+package com.ruoyi.auth.controller;
+
+import java.security.Principal;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 身份信息获取
+ *
+ * @author ruoyi
+ */
+@RestController
+@RequestMapping("/oauth")
+public class UserController
+{
+ @RequestMapping("/user")
+ public Principal user(Principal user)
+ {
+ return user;
+ }
+}
diff --git a/ruoyi-auth/src/main/java/com/ruoyi/auth/exception/CustomWebResponseExceptionTranslator.java b/ruoyi-auth/src/main/java/com/ruoyi/auth/exception/CustomWebResponseExceptionTranslator.java
new file mode 100644
index 0000000..7291cef
--- /dev/null
+++ b/ruoyi-auth/src/main/java/com/ruoyi/auth/exception/CustomWebResponseExceptionTranslator.java
@@ -0,0 +1,21 @@
+package com.ruoyi.auth.exception;
+
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
+import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
+
+/**
+ * OAuth2 自定义异常处理
+ *
+ * @author ruoyi
+ */
+public class CustomWebResponseExceptionTranslator implements WebResponseExceptionTranslator<OAuth2Exception>
+{
+ @Override
+ public ResponseEntity<OAuth2Exception> translate(Exception e)
+ {
+ OAuth2Exception oAuth2Exception = (OAuth2Exception) e;
+ return ResponseEntity.status(HttpServletResponse.SC_UNAUTHORIZED).body(oAuth2Exception);
+ }
+}
diff --git a/ruoyi-auth/src/main/resources/banner.txt b/ruoyi-auth/src/main/resources/banner.txt
new file mode 100644
index 0000000..d4fe7e1
--- /dev/null
+++ b/ruoyi-auth/src/main/resources/banner.txt
@@ -0,0 +1,10 @@
+Spring Boot Version: ${spring-boot.version}
+Spring Application Name: ${spring.application.name}
+ _ _ _
+ (_) | | | |
+ _ __ _ _ ___ _ _ _ ______ __ _ _ _ | |_ | |__
+| '__|| | | | / _ \ | | | || ||______| / _` || | | || __|| '_ \
+| | | |_| || (_) || |_| || | | (_| || |_| || |_ | | | |
+|_| \__,_| \___/ \__, ||_| \__,_| \__,_| \__||_| |_|
+ __/ |
+ |___/
\ No newline at end of file
diff --git a/ruoyi-auth/src/main/resources/bootstrap.yml b/ruoyi-auth/src/main/resources/bootstrap.yml
new file mode 100644
index 0000000..037cd7a
--- /dev/null
+++ b/ruoyi-auth/src/main/resources/bootstrap.yml
@@ -0,0 +1,24 @@
+# Tomcat
+server:
+ port: 9200
+
+# Spring
+spring:
+ application:
+ # 应用名称
+ name: ruoyi-auth
+ profiles:
+ # 环境配置
+ active: dev
+ cloud:
+ nacos:
+ discovery:
+ # 服务注册地址
+ server-addr: 127.0.0.1:8848
+ config:
+ # 配置中心地址
+ server-addr: 127.0.0.1:8848
+ # 配置文件格式
+ file-extension: yml
+ # 共享配置
+ shared-dataids: application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml
new file mode 100644
index 0000000..eb3a0e5
--- /dev/null
+++ b/ruoyi-common/pom.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi</artifactId>
+ <version>1.0.0</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <modules>
+ <module>ruoyi-common-log</module>
+ <module>ruoyi-common-core</module>
+ <module>ruoyi-common-redis</module>
+ <module>ruoyi-common-swagger</module>
+ <module>ruoyi-common-security</module>
+ <module>ruoyi-common-datascope</module>
+ </modules>
+
+ <artifactId>ruoyi-common</artifactId>
+ <packaging>pom</packaging>
+
+ <description>
+ ruoyi-common通用模块
+ </description>
+
+</project>
diff --git a/ruoyi-common/ruoyi-common-core/pom.xml b/ruoyi-common/ruoyi-common-core/pom.xml
new file mode 100644
index 0000000..afb5909
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/pom.xml
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns="http://maven.apache.org/POM/4.0.0"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-common</artifactId>
+ <version>1.0.0</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>ruoyi-common-core</artifactId>
+
+ <description>
+ ruoyi-common-core核心模块
+ </description>
+
+ <dependencies>
+
+ <!-- SpringCloud Openfeign -->
+ <dependency>
+ <groupId>org.springframework.cloud</groupId>
+ <artifactId>spring-cloud-starter-openfeign</artifactId>
+ </dependency>
+
+ <!-- Spring Context Support -->
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context-support</artifactId>
+ </dependency>
+
+ <!-- Spring Web -->
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-web</artifactId>
+ </dependency>
+
+ <!-- Apache Commons Pool2 -->
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-pool2</artifactId>
+ </dependency>
+
+ <!-- Pagehelper -->
+ <dependency>
+ <groupId>com.github.pagehelper</groupId>
+ <artifactId>pagehelper-spring-boot-starter</artifactId>
+ </dependency>
+
+ <!-- Java Validation -->
+ <dependency>
+ <groupId>javax.validation</groupId>
+ <artifactId>validation-api</artifactId>
+ </dependency>
+
+ <!-- Jackson -->
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ </dependency>
+
+ <!-- Alibaba Fastjson -->
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>fastjson</artifactId>
+ </dependency>
+
+ <!-- Apache Lang3 -->
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ </dependency>
+
+ <!-- Commons Io -->
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ </dependency>
+
+ <!-- Commons Fileupload -->
+ <dependency>
+ <groupId>commons-fileupload</groupId>
+ <artifactId>commons-fileupload</artifactId>
+ </dependency>
+
+ <!-- excel工具 -->
+ <dependency>
+ <groupId>org.apache.poi</groupId>
+ <artifactId>poi-ooxml</artifactId>
+ </dependency>
+
+ <!-- Java Servlet -->
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>javax.servlet-api</artifactId>
+ </dependency>
+
+ <!-- Swagger -->
+ <dependency>
+ <groupId>io.swagger</groupId>
+ <artifactId>swagger-annotations</artifactId>
+ </dependency>
+
+ </dependencies>
+
+</project>
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/annotation/Excel.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/annotation/Excel.java
new file mode 100644
index 0000000..fa7f6eb
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/annotation/Excel.java
@@ -0,0 +1,113 @@
+package com.ruoyi.common.core.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 自定义导出Excel数据注解
+ *
+ * @author ruoyi
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface Excel
+{
+ /**
+ * 导出到Excel中的名字.
+ */
+ public String name() default "";
+
+ /**
+ * 日期格式, 如: yyyy-MM-dd
+ */
+ public String dateFormat() default "";
+
+ /**
+ * 读取内容转表达式 (如: 0=男,1=女,2=未知)
+ */
+ public String readConverterExp() default "";
+
+ /**
+ * 导出类型(0数字 1字符串)
+ */
+ public ColumnType cellType() default ColumnType.STRING;
+
+ /**
+ * 导出时在excel中每个列的高度 单位为字符
+ */
+ public double height() default 14;
+
+ /**
+ * 导出时在excel中每个列的宽 单位为字符
+ */
+ public double width() default 16;
+
+ /**
+ * 文字后缀,如% 90 变成90%
+ */
+ public String suffix() default "";
+
+ /**
+ * 当值为空时,字段的默认值
+ */
+ public String defaultValue() default "";
+
+ /**
+ * 提示信息
+ */
+ public String prompt() default "";
+
+ /**
+ * 设置只能选择不能输入的列内容.
+ */
+ public String[] combo() default {};
+
+ /**
+ * 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写.
+ */
+ public boolean isExport() default true;
+
+ /**
+ * 另一个类中的属性名称,支持多级获取,以小数点隔开
+ */
+ public String targetAttr() default "";
+
+ /**
+ * 字段类型(0:导出导入;1:仅导出;2:仅导入)
+ */
+ Type type() default Type.ALL;
+
+ public enum Type
+ {
+ ALL(0), EXPORT(1), IMPORT(2);
+ private final int value;
+
+ Type(int value)
+ {
+ this.value = value;
+ }
+
+ public int value()
+ {
+ return this.value;
+ }
+ }
+
+ public enum ColumnType
+ {
+ NUMERIC(0), STRING(1);
+ private final int value;
+
+ ColumnType(int value)
+ {
+ this.value = value;
+ }
+
+ public int value()
+ {
+ return this.value;
+ }
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/annotation/Excels.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/annotation/Excels.java
new file mode 100644
index 0000000..af2d950
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/annotation/Excels.java
@@ -0,0 +1,18 @@
+package com.ruoyi.common.core.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Excel注解集
+ *
+ * @author ruoyi
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Excels
+{
+ Excel[] value();
+}
\ No newline at end of file
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/CacheConstants.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/CacheConstants.java
new file mode 100644
index 0000000..c43465b
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/CacheConstants.java
@@ -0,0 +1,19 @@
+package com.ruoyi.common.core.constant;
+
+/**
+ * 缓存的key 常量
+ *
+ * @author ruoyi
+ */
+public class CacheConstants
+{
+ /**
+ * oauth 缓存前缀
+ */
+ public static final String OAUTH_ACCESS = "oauth:access:";
+
+ /**
+ * oauth 客户端信息
+ */
+ public static final String CLIENT_DETAILS_KEY = "oauth:client:details";
+}
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/Constants.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/Constants.java
new file mode 100644
index 0000000..e8632d6
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/Constants.java
@@ -0,0 +1,98 @@
+package com.ruoyi.common.core.constant;
+
+/**
+ * 通用常量信息
+ *
+ * @author ruoyi
+ */
+public class Constants
+{
+ /**
+ * UTF-8 字符集
+ */
+ public static final String UTF8 = "UTF-8";
+
+ /**
+ * 成功标记
+ */
+ public static final Integer SUCCESS = 200;
+ /**
+ * 失败标记
+ */
+ public static final Integer FAIL = 501;
+
+ /**
+ * 登录成功
+ */
+ public static final String LOGIN_SUCCESS = "Success";
+
+ /**
+ * 注销
+ */
+ public static final String LOGOUT = "Logout";
+
+ /**
+ * 注册
+ */
+ public static final String REGISTER = "Register";
+
+ /**
+ * 登录失败
+ */
+ public static final String LOGIN_FAIL = "Error";
+
+ /**
+ * 当前记录起始索引
+ */
+ 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";
+
+ /**
+ * 参数管理 cache name
+ */
+ public static final String SYS_CONFIG_CACHE = "sys-config";
+
+ /**
+ * 参数管理 cache key
+ */
+ public static final String SYS_CONFIG_KEY = "sys_config:";
+
+ /**
+ * 字典管理 cache name
+ */
+ public static final String SYS_DICT_CACHE = "sys-dict";
+
+ /**
+ * 字典管理 cache key
+ */
+ public static final String SYS_DICT_KEY = "sys_dict:";
+
+ /**
+ * 验证码 redis key
+ */
+ public static final String CAPTCHA_CODE_KEY = "captcha_codes:";
+
+ /**
+ * 验证码有效期(分钟)
+ */
+ public static final Integer CAPTCHA_EXPIRATION = 2;
+
+ /**
+ * 资源映射路径 前缀
+ */
+ public static final String RESOURCE_PREFIX = "/profile";
+}
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/GenConstants.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/GenConstants.java
new file mode 100644
index 0000000..5b0a2c9
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/GenConstants.java
@@ -0,0 +1,94 @@
+package com.ruoyi.common.core.constant;
+
+/**
+ * 代码生成通用常量
+ *
+ * @author ruoyi
+ */
+public class GenConstants
+{
+ /** 单表(增删改查) */
+ public static final String TPL_CRUD = "crud";
+
+ /** 树表(增删改查) */
+ public static final String TPL_TREE = "tree";
+
+ /** 树编码字段 */
+ public static final String TREE_CODE = "treeCode";
+
+ /** 树父编码字段 */
+ public static final String TREE_PARENT_CODE = "treeParentCode";
+
+ /** 树名称字段 */
+ public static final String TREE_NAME = "treeName";
+
+ /** 数据库字符串类型 */
+ public static final String[] COLUMNTYPE_STR = { "char", "varchar", "narchar", "varchar2", "tinytext", "text",
+ "mediumtext", "longtext" };
+
+ /** 数据库时间类型 */
+ public static final String[] COLUMNTYPE_TIME = { "datetime", "time", "date", "timestamp" };
+
+ /** 数据库数字类型 */
+ public static final String[] COLUMNTYPE_NUMBER = { "tinyint", "smallint", "mediumint", "int", "number", "integer",
+ "bigint", "float", "float", "double", "decimal" };
+
+ /** 页面不需要编辑字段 */
+ public static final String[] COLUMNNAME_NOT_EDIT = { "id", "create_by", "create_time", "del_flag" };
+
+ /** 页面不需要显示的列表字段 */
+ public static final String[] COLUMNNAME_NOT_LIST = { "id", "create_by", "create_time", "del_flag", "update_by",
+ "update_time" };
+
+ /** 页面不需要查询字段 */
+ public static final String[] COLUMNNAME_NOT_QUERY = { "id", "create_by", "create_time", "del_flag", "update_by",
+ "update_time", "remark" };
+
+ /** Entity基类字段 */
+ public static final String[] BASE_ENTITY = { "createBy", "createTime", "updateBy", "updateTime", "remark" };
+
+ /** Tree基类字段 */
+ public static final String[] TREE_ENTITY = { "parentName", "parentId", "orderNum", "ancestors" };
+
+ /** 文本框 */
+ public static final String HTML_INPUT = "input";
+
+ /** 文本域 */
+ public static final String HTML_TEXTAREA = "textarea";
+
+ /** 下拉框 */
+ public static final String HTML_SELECT = "select";
+
+ /** 单选框 */
+ public static final String HTML_RADIO = "radio";
+
+ /** 复选框 */
+ public static final String HTML_CHECKBOX = "checkbox";
+
+ /** 日期控件 */
+ public static final String HTML_DATETIME = "datetime";
+
+ /** 字符串类型 */
+ public static final String TYPE_STRING = "String";
+
+ /** 整型 */
+ public static final String TYPE_INTEGER = "Integer";
+
+ /** 长整型 */
+ public static final String TYPE_LONG = "Long";
+
+ /** 浮点型 */
+ public static final String TYPE_DOUBLE = "Double";
+
+ /** 高精度计算类型 */
+ public static final String TYPE_BIGDECIMAL = "BigDecimal";
+
+ /** 时间类型 */
+ public static final String TYPE_DATE = "Date";
+
+ /** 模糊查询 */
+ public static final String QUERY_LIKE = "LIKE";
+
+ /** 需要 */
+ public static final String REQUIRE = "1";
+}
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/HttpStatus.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/HttpStatus.java
new file mode 100644
index 0000000..0de3490
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/HttpStatus.java
@@ -0,0 +1,89 @@
+package com.ruoyi.common.core.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-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/ScheduleConstants.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/ScheduleConstants.java
new file mode 100644
index 0000000..342cfe6
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/ScheduleConstants.java
@@ -0,0 +1,50 @@
+package com.ruoyi.common.core.constant;
+
+/**
+ * 任务调度通用常量
+ *
+ * @author ruoyi
+ */
+public interface ScheduleConstants
+{
+ public static final String TASK_CLASS_NAME = "TASK_CLASS_NAME";
+
+ /** 执行目标key */
+ public static final String TASK_PROPERTIES = "TASK_PROPERTIES";
+
+ /** 默认 */
+ public static final String MISFIRE_DEFAULT = "0";
+
+ /** 立即触发执行 */
+ public static final String MISFIRE_IGNORE_MISFIRES = "1";
+
+ /** 触发一次执行 */
+ public static final String MISFIRE_FIRE_AND_PROCEED = "2";
+
+ /** 不触发立即执行 */
+ public static final String MISFIRE_DO_NOTHING = "3";
+
+ public enum Status
+ {
+ /**
+ * 正常
+ */
+ NORMAL("0"),
+ /**
+ * 暂停
+ */
+ PAUSE("1");
+
+ private String value;
+
+ private Status(String value)
+ {
+ this.value = value;
+ }
+
+ public String getValue()
+ {
+ return value;
+ }
+ }
+}
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/SecurityConstants.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/SecurityConstants.java
new file mode 100644
index 0000000..d0c5cbb
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/SecurityConstants.java
@@ -0,0 +1,50 @@
+package com.ruoyi.common.core.constant;
+
+/**
+ * 权限相关通用常量
+ *
+ * @author ruoyi
+ */
+public class SecurityConstants
+{
+ /**
+ * 授权token url
+ */
+ public static final String AUTH_TOKEN = "/oauth/token";
+ /**
+ * 注销token url
+ */
+ public static final String TOKEN_LOGOUT = "/token/logout";
+
+ /**
+ * 用户ID字段
+ */
+ public static final String DETAILS_USER_ID = "user_id";
+
+ /**
+ * 用户名字段
+ */
+ public static final String DETAILS_USERNAME = "username";
+
+ /**
+ * sys_oauth_client_details 表的字段,不包括client_id、client_secret
+ */
+ public static final String CLIENT_FIELDS = "client_id, client_secret, resource_ids, scope, "
+ + "authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, "
+ + "refresh_token_validity, additional_information, autoapprove";
+
+ /**
+ * JdbcClientDetailsService 查询语句
+ */
+ public static final String BASE_FIND_STATEMENT = "select " + CLIENT_FIELDS + " from sys_oauth_client_details";
+
+ /**
+ * 按条件client_id 查询
+ */
+ public static final String DEFAULT_SELECT_STATEMENT = BASE_FIND_STATEMENT + " where client_id = ?";
+
+ /**
+ * 默认的查询语句
+ */
+ public static final String DEFAULT_FIND_STATEMENT = BASE_FIND_STATEMENT + " order by client_id";
+}
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/ServiceNameConstants.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/ServiceNameConstants.java
new file mode 100644
index 0000000..ba8d564
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/ServiceNameConstants.java
@@ -0,0 +1,19 @@
+package com.ruoyi.common.core.constant;
+
+/**
+ * 服务名称
+ *
+ * @author ruoyi
+ */
+public interface ServiceNameConstants
+{
+ /**
+ * 认证服务的serviceid
+ */
+ public static final String AUTH_SERVICE = "ruoyi-auth";
+
+ /**
+ * 系统模块的serviceid
+ */
+ public static final String SYSTEM_SERVICE = "ruoyi-system";
+}
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/UserConstants.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/UserConstants.java
new file mode 100644
index 0000000..aac2477
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/UserConstants.java
@@ -0,0 +1,42 @@
+package com.ruoyi.common.core.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_DISABLE = "1";
+
+ /** 角色封禁状态 */
+ public static final String ROLE_DISABLE = "1";
+
+ /** 部门正常状态 */
+ public static final String DEPT_NORMAL = "0";
+
+ /** 部门停用状态 */
+ public static final String DEPT_DISABLE = "1";
+
+ /** 字典正常状态 */
+ 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-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/R.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/R.java
new file mode 100644
index 0000000..945a16b
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/domain/R.java
@@ -0,0 +1,99 @@
+package com.ruoyi.common.core.domain;
+
+import java.io.Serializable;
+import com.ruoyi.common.core.constant.Constants;
+
+/**
+ * 响应信息主体
+ *
+ * @author ruoyi
+ */
+public class R<T> implements Serializable
+{
+ private static final long serialVersionUID = 1L;
+
+ private int code;
+
+ private String msg;
+
+ private T data;
+
+ public static <T> R<T> ok()
+ {
+ return restResult(null, Constants.SUCCESS, null);
+ }
+
+ public static <T> R<T> ok(T data)
+ {
+ return restResult(data, Constants.SUCCESS, null);
+ }
+
+ public static <T> R<T> ok(T data, String msg)
+ {
+ return restResult(data, Constants.SUCCESS, msg);
+ }
+
+ public static <T> R<T> failed()
+ {
+ return restResult(null, Constants.FAIL, null);
+ }
+
+ public static <T> R<T> failed(String msg)
+ {
+ return restResult(null, Constants.FAIL, msg);
+ }
+
+ public static <T> R<T> failed(T data)
+ {
+ return restResult(data, Constants.FAIL, null);
+ }
+
+ public static <T> R<T> failed(T data, String msg)
+ {
+ return restResult(data, Constants.FAIL, msg);
+ }
+
+ public static <T> R<T> failed(int code, String msg)
+ {
+ return restResult(null, code, msg);
+ }
+
+ private static <T> R<T> restResult(T data, int code, String msg)
+ {
+ R<T> apiResult = new R<>();
+ apiResult.setCode(code);
+ apiResult.setData(data);
+ apiResult.setMsg(msg);
+ return apiResult;
+ }
+
+ public int getCode()
+ {
+ return code;
+ }
+
+ public void setCode(int code)
+ {
+ this.code = code;
+ }
+
+ public String getMsg()
+ {
+ return msg;
+ }
+
+ public void setMsg(String msg)
+ {
+ this.msg = msg;
+ }
+
+ public T getData()
+ {
+ return data;
+ }
+
+ public void setData(T data)
+ {
+ this.data = data;
+ }
+}
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/enums/UserStatus.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/enums/UserStatus.java
new file mode 100644
index 0000000..9ff8496
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/enums/UserStatus.java
@@ -0,0 +1,30 @@
+package com.ruoyi.common.core.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-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/BaseException.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/BaseException.java
new file mode 100644
index 0000000..5114521
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/BaseException.java
@@ -0,0 +1,79 @@
+package com.ruoyi.common.core.exception;
+
+/**
+ * 基础异常
+ *
+ * @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);
+ }
+
+ public String getModule()
+ {
+ return module;
+ }
+
+ public String getCode()
+ {
+ return code;
+ }
+
+ public Object[] getArgs()
+ {
+ return args;
+ }
+
+ public String getDefaultMessage()
+ {
+ return defaultMessage;
+ }
+}
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/CaptchaException.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/CaptchaException.java
new file mode 100644
index 0000000..3e01851
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/CaptchaException.java
@@ -0,0 +1,16 @@
+package com.ruoyi.common.core.exception;
+
+/**
+ * 验证码错误异常类
+ *
+ * @author ruoyi
+ */
+public class CaptchaException extends RuntimeException
+{
+ private static final long serialVersionUID = 1L;
+
+ public CaptchaException(String msg)
+ {
+ super(msg);
+ }
+}
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/CheckedException.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/CheckedException.java
new file mode 100644
index 0000000..862eefa
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/CheckedException.java
@@ -0,0 +1,31 @@
+package com.ruoyi.common.core.exception;
+
+/**
+ * 检查异常
+ *
+ * @author ruoyi
+ */
+public class CheckedException extends RuntimeException
+{
+ private static final long serialVersionUID = 1L;
+
+ public CheckedException(String message)
+ {
+ super(message);
+ }
+
+ public CheckedException(Throwable cause)
+ {
+ super(cause);
+ }
+
+ public CheckedException(String message, Throwable cause)
+ {
+ super(message, cause);
+ }
+
+ public CheckedException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace)
+ {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+}
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/CustomException.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/CustomException.java
new file mode 100644
index 0000000..38b727b
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/CustomException.java
@@ -0,0 +1,43 @@
+package com.ruoyi.common.core.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-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/DemoModeException.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/DemoModeException.java
new file mode 100644
index 0000000..ac2a19e
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/DemoModeException.java
@@ -0,0 +1,15 @@
+package com.ruoyi.common.core.exception;
+
+/**
+ * 演示模式异常
+ *
+ * @author ruoyi
+ */
+public class DemoModeException extends RuntimeException
+{
+ private static final long serialVersionUID = 1L;
+
+ public DemoModeException()
+ {
+ }
+}
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/UtilException.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/UtilException.java
new file mode 100644
index 0000000..a1c16b5
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/UtilException.java
@@ -0,0 +1,26 @@
+package com.ruoyi.common.core.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-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/job/TaskException.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/job/TaskException.java
new file mode 100644
index 0000000..c98397c
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/job/TaskException.java
@@ -0,0 +1,34 @@
+package com.ruoyi.common.core.exception.job;
+
+/**
+ * 计划策略异常
+ *
+ * @author ruoyi
+ */
+public class TaskException extends Exception
+{
+ private static final long serialVersionUID = 1L;
+
+ private Code code;
+
+ public TaskException(String msg, Code code)
+ {
+ this(msg, code, null);
+ }
+
+ public TaskException(String msg, Code code, Exception nestedEx)
+ {
+ super(msg, nestedEx);
+ this.code = code;
+ }
+
+ public Code getCode()
+ {
+ return code;
+ }
+
+ public enum Code
+ {
+ TASK_EXISTS, NO_TASK_EXISTS, TASK_ALREADY_STARTED, UNKNOWN, CONFIG_ERROR, TASK_NODE_NOT_AVAILABLE
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/user/CaptchaExpireException.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/user/CaptchaExpireException.java
new file mode 100644
index 0000000..6924d7e
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/user/CaptchaExpireException.java
@@ -0,0 +1,16 @@
+package com.ruoyi.common.core.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-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/user/UserException.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/user/UserException.java
new file mode 100644
index 0000000..cf51f49
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/user/UserException.java
@@ -0,0 +1,18 @@
+package com.ruoyi.common.core.exception.user;
+
+import com.ruoyi.common.core.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-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/user/UserPasswordNotMatchException.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/user/UserPasswordNotMatchException.java
new file mode 100644
index 0000000..7f514a6
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/user/UserPasswordNotMatchException.java
@@ -0,0 +1,16 @@
+package com.ruoyi.common.core.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-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/text/CharsetKit.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/text/CharsetKit.java
new file mode 100644
index 0000000..31eef05
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/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.core.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-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/text/Convert.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/text/Convert.java
new file mode 100644
index 0000000..1c87e75
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/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.core.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-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/text/StrFormatter.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/text/StrFormatter.java
new file mode 100644
index 0000000..a7d4d84
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/text/StrFormatter.java
@@ -0,0 +1,92 @@
+package com.ruoyi.common.core.text;
+
+import com.ruoyi.common.core.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-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/text/UUID.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/text/UUID.java
new file mode 100644
index 0000000..a1ba258
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/text/UUID.java
@@ -0,0 +1,481 @@
+package com.ruoyi.common.core.text;
+
+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.core.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-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/DateUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/DateUtils.java
new file mode 100644
index 0000000..9b9ecc5
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/DateUtils.java
@@ -0,0 +1,155 @@
+package com.ruoyi.common.core.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-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ExceptionUtil.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ExceptionUtil.java
new file mode 100644
index 0000000..b043af1
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ExceptionUtil.java
@@ -0,0 +1,40 @@
+package com.ruoyi.common.core.utils;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import org.apache.commons.lang3.exception.ExceptionUtils;
+
+/**
+ * 错误信息处理类。
+ *
+ * @author ruoyi
+ */
+public class ExceptionUtil
+{
+ /**
+ * 获取exception的详细错误信息。
+ */
+ public static String getExceptionMessage(Throwable e)
+ {
+ StringWriter sw = new StringWriter();
+ e.printStackTrace(new PrintWriter(sw, true));
+ String str = sw.toString();
+ return str;
+ }
+
+ public static String getRootErrorMseeage(Exception e)
+ {
+ Throwable root = ExceptionUtils.getRootCause(e);
+ root = (root == null ? e : root);
+ if (root == null)
+ {
+ return "";
+ }
+ String msg = root.getMessage();
+ if (msg == null)
+ {
+ return "null";
+ }
+ return StringUtils.defaultString(msg);
+ }
+}
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/IdUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/IdUtils.java
new file mode 100644
index 0000000..4d38792
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/IdUtils.java
@@ -0,0 +1,51 @@
+package com.ruoyi.common.core.utils;
+
+import com.ruoyi.common.core.text.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-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ReUtil.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ReUtil.java
new file mode 100644
index 0000000..466d14f
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ReUtil.java
@@ -0,0 +1,164 @@
+package com.ruoyi.common.core.utils;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.ruoyi.common.core.text.Convert;
+import com.ruoyi.common.core.utils.StringUtils;
+
+public class ReUtil
+{
+ public final static Pattern GROUP_VAR = Pattern.compile("\\$(\\d+)");
+
+ /**
+ * 正则中需要被转义的关键字
+ */
+ public final static Set<Character> RE_KEYS = new HashSet<>(
+ Arrays.asList('$', '(', ')', '*', '+', '.', '[', ']', '?', '\\', '^', '{', '}', '|'));;
+
+ /**
+ * 正则替换指定值<br>
+ * 通过正则查找到字符串,然后把匹配到的字符串加入到replacementTemplate中,$1表示分组1的字符串
+ *
+ * <p>
+ * 例如:原字符串是:中文1234,我想把1234换成(1234),则可以:
+ *
+ * <pre>
+ * ReUtil.replaceAll("中文1234", "(\\d+)", "($1)"))
+ *
+ * 结果:中文(1234)
+ * </pre>
+ *
+ * @param content 文本
+ * @param regex 正则
+ * @param replacementTemplate 替换的文本模板,可以使用$1类似的变量提取正则匹配出的内容
+ * @return 处理后的文本
+ */
+ public static String replaceAll(CharSequence content, String regex, String replacementTemplate)
+ {
+ final Pattern pattern = Pattern.compile(regex, Pattern.DOTALL);
+ return replaceAll(content, pattern, replacementTemplate);
+ }
+
+ /**
+ * 正则替换指定值<br>
+ * 通过正则查找到字符串,然后把匹配到的字符串加入到replacementTemplate中,$1表示分组1的字符串
+ *
+ * @param content 文本
+ * @param pattern {@link Pattern}
+ * @param replacementTemplate 替换的文本模板,可以使用$1类似的变量提取正则匹配出的内容
+ * @return 处理后的文本
+ * @since 3.0.4
+ */
+ public static String replaceAll(CharSequence content, Pattern pattern, String replacementTemplate)
+ {
+ if (StringUtils.isEmpty(content))
+ {
+ return StringUtils.EMPTY;
+ }
+
+ final Matcher matcher = pattern.matcher(content);
+ boolean result = matcher.find();
+ if (result)
+ {
+ final Set<String> varNums = findAll(GROUP_VAR, replacementTemplate, 1, new HashSet<>());
+ final StringBuffer sb = new StringBuffer();
+ do
+ {
+ String replacement = replacementTemplate;
+ for (String var : varNums)
+ {
+ int group = Integer.parseInt(var);
+ replacement = replacement.replace("$" + var, matcher.group(group));
+ }
+ matcher.appendReplacement(sb, escape(replacement));
+ result = matcher.find();
+ }
+ while (result);
+ matcher.appendTail(sb);
+ return sb.toString();
+ }
+ return Convert.toStr(content);
+ }
+
+ /**
+ * 取得内容中匹配的所有结果
+ *
+ * @param <T> 集合类型
+ * @param pattern 编译后的正则模式
+ * @param content 被查找的内容
+ * @param group 正则的分组
+ * @param collection 返回的集合类型
+ * @return 结果集
+ */
+ public static <T extends Collection<String>> T findAll(Pattern pattern, CharSequence content, int group,
+ T collection)
+ {
+ if (null == pattern || null == content)
+ {
+ return null;
+ }
+
+ if (null == collection)
+ {
+ throw new NullPointerException("Null collection param provided!");
+ }
+
+ final Matcher matcher = pattern.matcher(content);
+ while (matcher.find())
+ {
+ collection.add(matcher.group(group));
+ }
+ return collection;
+ }
+
+ /**
+ * 转义字符,将正则的关键字转义
+ *
+ * @param c 字符
+ * @return 转义后的文本
+ */
+ public static String escape(char c)
+ {
+ final StringBuilder builder = new StringBuilder();
+ if (RE_KEYS.contains(c))
+ {
+ builder.append('\\');
+ }
+ builder.append(c);
+ return builder.toString();
+ }
+
+ /**
+ * 转义字符串,将正则的关键字转义
+ *
+ * @param content 文本
+ * @return 转义后的文本
+ */
+ public static String escape(CharSequence content)
+ {
+ if (StringUtils.isBlank(content))
+ {
+ return StringUtils.EMPTY;
+ }
+
+ final StringBuilder builder = new StringBuilder();
+ int len = content.length();
+ char current;
+ for (int i = 0; i < len; i++)
+ {
+ current = content.charAt(i);
+ if (RE_KEYS.contains(current))
+ {
+ builder.append('\\');
+ }
+ builder.append(current);
+ }
+ return builder.toString();
+ }
+
+}
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ServletUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ServletUtils.java
new file mode 100644
index 0000000..7de6fcb
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ServletUtils.java
@@ -0,0 +1,136 @@
+package com.ruoyi.common.core.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-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/SpringUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/SpringUtils.java
new file mode 100644
index 0000000..9dcbceb
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/SpringUtils.java
@@ -0,0 +1,114 @@
+package com.ruoyi.common.core.utils;
+
+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-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/StringUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/StringUtils.java
new file mode 100644
index 0000000..a0d0c91
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/StringUtils.java
@@ -0,0 +1,404 @@
+package com.ruoyi.common.core.utils;
+
+import java.util.Collection;
+import java.util.Map;
+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);
+ }
+
+ /**
+ * 下划线转驼峰命名
+ */
+ 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();
+ }
+
+ @SuppressWarnings("unchecked")
+ public static <T> T cast(Object obj)
+ {
+ return (T) obj;
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/bean/BeanUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/bean/BeanUtils.java
new file mode 100644
index 0000000..d027cef
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/bean/BeanUtils.java
@@ -0,0 +1,110 @@
+package com.ruoyi.common.core.utils.bean;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Bean 工具类
+ *
+ * @author ruoyi
+ */
+public class BeanUtils extends org.springframework.beans.BeanUtils
+{
+ /** Bean方法名中属性名开始的下标 */
+ private static final int BEAN_METHOD_PROP_INDEX = 3;
+
+ /** * 匹配getter方法的正则表达式 */
+ private static final Pattern GET_PATTERN = Pattern.compile("get(\\p{javaUpperCase}\\w*)");
+
+ /** * 匹配setter方法的正则表达式 */
+ private static final Pattern SET_PATTERN = Pattern.compile("set(\\p{javaUpperCase}\\w*)");
+
+ /**
+ * Bean属性复制工具方法。
+ *
+ * @param dest 目标对象
+ * @param src 源对象
+ */
+ public static void copyBeanProp(Object dest, Object src)
+ {
+ try
+ {
+ copyProperties(src, dest);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * 获取对象的setter方法。
+ *
+ * @param obj 对象
+ * @return 对象的setter方法列表
+ */
+ public static List<Method> getSetterMethods(Object obj)
+ {
+ // setter方法列表
+ List<Method> setterMethods = new ArrayList<Method>();
+
+ // 获取所有方法
+ Method[] methods = obj.getClass().getMethods();
+
+ // 查找setter方法
+
+ for (Method method : methods)
+ {
+ Matcher m = SET_PATTERN.matcher(method.getName());
+ if (m.matches() && (method.getParameterTypes().length == 1))
+ {
+ setterMethods.add(method);
+ }
+ }
+ // 返回setter方法列表
+ return setterMethods;
+ }
+
+ /**
+ * 获取对象的getter方法。
+ *
+ * @param obj 对象
+ * @return 对象的getter方法列表
+ */
+
+ public static List<Method> getGetterMethods(Object obj)
+ {
+ // getter方法列表
+ List<Method> getterMethods = new ArrayList<Method>();
+ // 获取所有方法
+ Method[] methods = obj.getClass().getMethods();
+ // 查找getter方法
+ for (Method method : methods)
+ {
+ Matcher m = GET_PATTERN.matcher(method.getName());
+ if (m.matches() && (method.getParameterTypes().length == 0))
+ {
+ getterMethods.add(method);
+ }
+ }
+ // 返回getter方法列表
+ return getterMethods;
+ }
+
+ /**
+ * 检查Bean方法名中的属性名是否相等。<br>
+ * 如getName()和setName()属性名一样,getName()和setAge()属性名不一样。
+ *
+ * @param m1 方法名1
+ * @param m2 方法名2
+ * @return 属性名一样返回true,否则返回false
+ */
+
+ public static boolean isMethodPropEquals(String m1, String m2)
+ {
+ return m1.substring(BEAN_METHOD_PROP_INDEX).equals(m2.substring(BEAN_METHOD_PROP_INDEX));
+ }
+}
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ip/IpUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ip/IpUtils.java
new file mode 100644
index 0000000..600a154
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ip/IpUtils.java
@@ -0,0 +1,189 @@
+package com.ruoyi.common.core.utils.ip;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import javax.servlet.http.HttpServletRequest;
+import com.ruoyi.common.core.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-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/poi/ExcelUtil.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/poi/ExcelUtil.java
new file mode 100644
index 0000000..fbfd1dd
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/poi/ExcelUtil.java
@@ -0,0 +1,843 @@
+package com.ruoyi.common.core.utils.poi;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.math.BigDecimal;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.poi.hssf.usermodel.HSSFDateUtil;
+import org.apache.poi.ss.usermodel.BorderStyle;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellStyle;
+import org.apache.poi.ss.usermodel.CellType;
+import org.apache.poi.ss.usermodel.DataValidation;
+import org.apache.poi.ss.usermodel.DataValidationConstraint;
+import org.apache.poi.ss.usermodel.DataValidationHelper;
+import org.apache.poi.ss.usermodel.DateUtil;
+import org.apache.poi.ss.usermodel.FillPatternType;
+import org.apache.poi.ss.usermodel.Font;
+import org.apache.poi.ss.usermodel.HorizontalAlignment;
+import org.apache.poi.ss.usermodel.IndexedColors;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.VerticalAlignment;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.usermodel.WorkbookFactory;
+import org.apache.poi.ss.util.CellRangeAddressList;
+import org.apache.poi.xssf.streaming.SXSSFWorkbook;
+import org.apache.poi.xssf.usermodel.XSSFDataValidation;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import com.ruoyi.common.core.annotation.Excel;
+import com.ruoyi.common.core.annotation.Excel.ColumnType;
+import com.ruoyi.common.core.annotation.Excel.Type;
+import com.ruoyi.common.core.annotation.Excels;
+import com.ruoyi.common.core.text.Convert;
+import com.ruoyi.common.core.utils.DateUtils;
+import com.ruoyi.common.core.utils.StringUtils;
+import com.ruoyi.common.core.utils.reflect.ReflectUtils;
+
+/**
+ * Excel相关处理
+ *
+ * @author ruoyi
+ */
+public class ExcelUtil<T>
+{
+ private static final Logger log = LoggerFactory.getLogger(ExcelUtil.class);
+
+ /**
+ * Excel sheet最大行数,默认65536
+ */
+ public static final int sheetSize = 65536;
+
+ /**
+ * 工作表名称
+ */
+ private String sheetName;
+
+ /**
+ * 导出类型(EXPORT:导出数据;IMPORT:导入模板)
+ */
+ private Type type;
+
+ /**
+ * 工作薄对象
+ */
+ private Workbook wb;
+
+ /**
+ * 工作表对象
+ */
+ private Sheet sheet;
+
+ /**
+ * 样式列表
+ */
+ private Map<String, CellStyle> styles;
+
+ /**
+ * 导入导出数据列表
+ */
+ private List<T> list;
+
+ /**
+ * 注解列表
+ */
+ private List<Object[]> fields;
+
+ /**
+ * 实体对象
+ */
+ public Class<T> clazz;
+
+ public ExcelUtil(Class<T> clazz)
+ {
+ this.clazz = clazz;
+ }
+
+ public void init(List<T> list, String sheetName, Type type)
+ {
+ if (list == null)
+ {
+ list = new ArrayList<T>();
+ }
+ this.list = list;
+ this.sheetName = sheetName;
+ this.type = type;
+ createExcelField();
+ createWorkbook();
+ }
+
+ /**
+ * 对excel表单默认第一个索引名转换成list
+ *
+ * @param is 输入流
+ * @return 转换后集合
+ */
+ public List<T> importExcel(InputStream is) throws Exception
+ {
+ return importExcel(StringUtils.EMPTY, is);
+ }
+
+ /**
+ * 对excel表单指定表格索引名转换成list
+ *
+ * @param sheetName 表格索引名
+ * @param is 输入流
+ * @return 转换后集合
+ */
+ public List<T> importExcel(String sheetName, InputStream is) throws Exception
+ {
+ this.type = Type.IMPORT;
+ this.wb = WorkbookFactory.create(is);
+ List<T> list = new ArrayList<T>();
+ Sheet sheet = null;
+ if (StringUtils.isNotEmpty(sheetName))
+ {
+ // 如果指定sheet名,则取指定sheet中的内容.
+ sheet = wb.getSheet(sheetName);
+ }
+ else
+ {
+ // 如果传入的sheet名不存在则默认指向第1个sheet.
+ sheet = wb.getSheetAt(0);
+ }
+
+ if (sheet == null)
+ {
+ throw new IOException("文件sheet不存在");
+ }
+
+ int rows = sheet.getPhysicalNumberOfRows();
+
+ if (rows > 0)
+ {
+ // 定义一个map用于存放excel列的序号和field.
+ Map<String, Integer> cellMap = new HashMap<String, Integer>();
+ // 获取表头
+ Row heard = sheet.getRow(0);
+ for (int i = 0; i < heard.getPhysicalNumberOfCells(); i++)
+ {
+ Cell cell = heard.getCell(i);
+ if (StringUtils.isNotNull(cell))
+ {
+ String value = this.getCellValue(heard, i).toString();
+ cellMap.put(value, i);
+ }
+ else
+ {
+ cellMap.put(null, i);
+ }
+ }
+ // 有数据时才处理 得到类的所有field.
+ Field[] allFields = clazz.getDeclaredFields();
+ // 定义一个map用于存放列的序号和field.
+ Map<Integer, Field> fieldsMap = new HashMap<Integer, Field>();
+ for (int col = 0; col < allFields.length; col++)
+ {
+ Field field = allFields[col];
+ Excel attr = field.getAnnotation(Excel.class);
+ if (attr != null && (attr.type() == Type.ALL || attr.type() == type))
+ {
+ // 设置类的私有字段属性可访问.
+ field.setAccessible(true);
+ Integer column = cellMap.get(attr.name());
+ fieldsMap.put(column, field);
+ }
+ }
+ for (int i = 1; i < rows; i++)
+ {
+ // 从第2行开始取数据,默认第一行是表头.
+ Row row = sheet.getRow(i);
+ T entity = null;
+ for (Map.Entry<Integer, Field> entry : fieldsMap.entrySet())
+ {
+ Object val = this.getCellValue(row, entry.getKey());
+
+ // 如果不存在实例则新建.
+ entity = (entity == null ? clazz.newInstance() : entity);
+ // 从map中得到对应列的field.
+ Field field = fieldsMap.get(entry.getKey());
+ // 取得类型,并根据对象类型设置值.
+ Class<?> fieldType = field.getType();
+ if (String.class == fieldType)
+ {
+ String s = Convert.toStr(val);
+ if (StringUtils.endsWith(s, ".0"))
+ {
+ val = StringUtils.substringBefore(s, ".0");
+ }
+ else
+ {
+ val = Convert.toStr(val);
+ }
+ }
+ else if ((Integer.TYPE == fieldType) || (Integer.class == fieldType))
+ {
+ val = Convert.toInt(val);
+ }
+ else if ((Long.TYPE == fieldType) || (Long.class == fieldType))
+ {
+ val = Convert.toLong(val);
+ }
+ else if ((Double.TYPE == fieldType) || (Double.class == fieldType))
+ {
+ val = Convert.toDouble(val);
+ }
+ else if ((Float.TYPE == fieldType) || (Float.class == fieldType))
+ {
+ val = Convert.toFloat(val);
+ }
+ else if (BigDecimal.class == fieldType)
+ {
+ val = Convert.toBigDecimal(val);
+ }
+ else if (Date.class == fieldType)
+ {
+ if (val instanceof String)
+ {
+ val = DateUtils.parseDate(val);
+ }
+ else if (val instanceof Double)
+ {
+ val = DateUtil.getJavaDate((Double) val);
+ }
+ }
+ if (StringUtils.isNotNull(fieldType))
+ {
+ Excel attr = field.getAnnotation(Excel.class);
+ String propertyName = field.getName();
+ if (StringUtils.isNotEmpty(attr.targetAttr()))
+ {
+ propertyName = field.getName() + "." + attr.targetAttr();
+ }
+ else if (StringUtils.isNotEmpty(attr.readConverterExp()))
+ {
+ val = reverseByExp(String.valueOf(val), attr.readConverterExp());
+ }
+ ReflectUtils.invokeSetter(entity, propertyName, val);
+ }
+ }
+ list.add(entity);
+ }
+ }
+ return list;
+ }
+
+ /**
+ * 对list数据源将其里面的数据导入到excel表单
+ *
+ * @param response 返回数据
+ * @param list 导出数据集合
+ * @param sheetName 工作表的名称
+ * @return 结果
+ * @throws IOException
+ */
+ public void exportExcel(HttpServletResponse response, List<T> list, String sheetName) throws IOException
+ {
+ response.setContentType("application/vnd.ms-excel");
+ response.setCharacterEncoding("utf-8");
+ this.init(list, sheetName, Type.EXPORT);
+ exportExcel(response.getOutputStream());
+ }
+
+ /**
+ * 对list数据源将其里面的数据导入到excel表单
+ *
+ * @param sheetName 工作表的名称
+ * @return 结果
+ */
+ public void importTemplateExcel(HttpServletResponse response, String sheetName) throws IOException
+ {
+ response.setContentType("application/vnd.ms-excel");
+ response.setCharacterEncoding("utf-8");
+ this.init(null, sheetName, Type.IMPORT);
+ exportExcel(response.getOutputStream());
+ }
+
+ /**
+ * 对list数据源将其里面的数据导入到excel表单
+ *
+ * @return 结果
+ */
+ public void exportExcel(OutputStream outputStream)
+ {
+ try
+ {
+ // 取出一共有多少个sheet.
+ double sheetNo = Math.ceil(list.size() / sheetSize);
+ for (int index = 0; index <= sheetNo; index++)
+ {
+ createSheet(sheetNo, index);
+
+ // 产生一行
+ Row row = sheet.createRow(0);
+ int column = 0;
+ // 写入各个字段的列头名称
+ for (Object[] os : fields)
+ {
+ Excel excel = (Excel) os[1];
+ this.createCell(excel, row, column++);
+ }
+ if (Type.EXPORT.equals(type))
+ {
+ fillExcelData(index, row);
+ }
+ }
+ wb.write(outputStream);
+ }
+ catch (Exception e)
+ {
+ log.error("导出Excel异常{}", e.getMessage());
+ }
+ finally
+ {
+ if (wb != null)
+ {
+ try
+ {
+ wb.close();
+ }
+ catch (IOException e1)
+ {
+ e1.printStackTrace();
+ }
+ }
+ if (outputStream != null)
+ {
+ try
+ {
+ outputStream.close();
+ }
+ catch (IOException e1)
+ {
+ e1.printStackTrace();
+ }
+ }
+ }
+ }
+
+ /**
+ * 填充excel数据
+ *
+ * @param index 序号
+ * @param row 单元格行
+ */
+ public void fillExcelData(int index, Row row)
+ {
+ int startNo = index * sheetSize;
+ int endNo = Math.min(startNo + sheetSize, list.size());
+ for (int i = startNo; i < endNo; i++)
+ {
+ row = sheet.createRow(i + 1 - startNo);
+ // 得到导出对象.
+ T vo = (T) list.get(i);
+ int column = 0;
+ for (Object[] os : fields)
+ {
+ Field field = (Field) os[0];
+ Excel excel = (Excel) os[1];
+ // 设置实体类私有属性可访问
+ field.setAccessible(true);
+ this.addCell(excel, row, vo, field, column++);
+ }
+ }
+ }
+
+ /**
+ * 创建表格样式
+ *
+ * @param wb 工作薄对象
+ * @return 样式列表
+ */
+ private Map<String, CellStyle> createStyles(Workbook wb)
+ {
+ // 写入各条记录,每条记录对应excel表中的一行
+ Map<String, CellStyle> styles = new HashMap<String, CellStyle>();
+ CellStyle style = wb.createCellStyle();
+ style.setAlignment(HorizontalAlignment.CENTER);
+ style.setVerticalAlignment(VerticalAlignment.CENTER);
+ style.setBorderRight(BorderStyle.THIN);
+ style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
+ style.setBorderLeft(BorderStyle.THIN);
+ style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
+ style.setBorderTop(BorderStyle.THIN);
+ style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
+ style.setBorderBottom(BorderStyle.THIN);
+ style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
+ Font dataFont = wb.createFont();
+ dataFont.setFontName("Arial");
+ dataFont.setFontHeightInPoints((short) 10);
+ style.setFont(dataFont);
+ styles.put("data", style);
+
+ style = wb.createCellStyle();
+ style.cloneStyleFrom(styles.get("data"));
+ style.setAlignment(HorizontalAlignment.CENTER);
+ style.setVerticalAlignment(VerticalAlignment.CENTER);
+ style.setFillForegroundColor(IndexedColors.GREY_50_PERCENT.getIndex());
+ style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+ Font headerFont = wb.createFont();
+ headerFont.setFontName("Arial");
+ headerFont.setFontHeightInPoints((short) 10);
+ headerFont.setBold(true);
+ headerFont.setColor(IndexedColors.WHITE.getIndex());
+ style.setFont(headerFont);
+ styles.put("header", style);
+
+ return styles;
+ }
+
+ /**
+ * 创建单元格
+ */
+ public Cell createCell(Excel attr, Row row, int column)
+ {
+ // 创建列
+ Cell cell = row.createCell(column);
+ // 写入列信息
+ cell.setCellValue(attr.name());
+ setDataValidation(attr, row, column);
+ cell.setCellStyle(styles.get("header"));
+ return cell;
+ }
+
+ /**
+ * 设置单元格信息
+ *
+ * @param value 单元格值
+ * @param attr 注解相关
+ * @param cell 单元格信息
+ */
+ public void setCellVo(Object value, Excel attr, Cell cell)
+ {
+ if (ColumnType.STRING == attr.cellType())
+ {
+ cell.setCellType(CellType.NUMERIC);
+ cell.setCellValue(StringUtils.isNull(value) ? attr.defaultValue() : value + attr.suffix());
+ }
+ else if (ColumnType.NUMERIC == attr.cellType())
+ {
+ cell.setCellType(CellType.NUMERIC);
+ cell.setCellValue(Integer.parseInt(value + ""));
+ }
+ }
+
+ /**
+ * 创建表格样式
+ */
+ public void setDataValidation(Excel attr, Row row, int column)
+ {
+ if (attr.name().indexOf("注:") >= 0)
+ {
+ sheet.setColumnWidth(column, 6000);
+ }
+ else
+ {
+ // 设置列宽
+ sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256));
+ row.setHeight((short) (attr.height() * 20));
+ }
+ // 如果设置了提示信息则鼠标放上去提示.
+ if (StringUtils.isNotEmpty(attr.prompt()))
+ {
+ // 这里默认设了2-101列提示.
+ setXSSFPrompt(sheet, "", attr.prompt(), 1, 100, column, column);
+ }
+ // 如果设置了combo属性则本列只能选择不能输入
+ if (attr.combo().length > 0)
+ {
+ // 这里默认设了2-101列只能选择不能输入.
+ setXSSFValidation(sheet, attr.combo(), 1, 100, column, column);
+ }
+ }
+
+ /**
+ * 添加单元格
+ */
+ public Cell addCell(Excel attr, Row row, T vo, Field field, int column)
+ {
+ Cell cell = null;
+ try
+ {
+ // 设置行高
+ row.setHeight((short) (attr.height() * 20));
+ // 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列.
+ if (attr.isExport())
+ {
+ // 创建cell
+ cell = row.createCell(column);
+ cell.setCellStyle(styles.get("data"));
+
+ // 用于读取对象中的属性
+ Object value = getTargetValue(vo, field, attr);
+ String dateFormat = attr.dateFormat();
+ String readConverterExp = attr.readConverterExp();
+ if (StringUtils.isNotEmpty(dateFormat) && StringUtils.isNotNull(value))
+ {
+ cell.setCellValue(DateUtils.parseDateToStr(dateFormat, (Date) value));
+ }
+ else if (StringUtils.isNotEmpty(readConverterExp) && StringUtils.isNotNull(value))
+ {
+ cell.setCellValue(convertByExp(String.valueOf(value), readConverterExp));
+ }
+ else
+ {
+ // 设置列类型
+ setCellVo(value, attr, cell);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ log.error("导出Excel失败{}", e);
+ }
+ return cell;
+ }
+
+ /**
+ * 设置 POI XSSFSheet 单元格提示
+ *
+ * @param sheet 表单
+ * @param promptTitle 提示标题
+ * @param promptContent 提示内容
+ * @param firstRow 开始行
+ * @param endRow 结束行
+ * @param firstCol 开始列
+ * @param endCol 结束列
+ */
+ public void setXSSFPrompt(Sheet sheet, String promptTitle, String promptContent, int firstRow, int endRow,
+ int firstCol, int endCol)
+ {
+ DataValidationHelper helper = sheet.getDataValidationHelper();
+ DataValidationConstraint constraint = helper.createCustomConstraint("DD1");
+ CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol);
+ DataValidation dataValidation = helper.createValidation(constraint, regions);
+ dataValidation.createPromptBox(promptTitle, promptContent);
+ dataValidation.setShowPromptBox(true);
+ sheet.addValidationData(dataValidation);
+ }
+
+ /**
+ * 设置某些列的值只能输入预制的数据,显示下拉框.
+ *
+ * @param sheet 要设置的sheet.
+ * @param textlist 下拉框显示的内容
+ * @param firstRow 开始行
+ * @param endRow 结束行
+ * @param firstCol 开始列
+ * @param endCol 结束列
+ * @return 设置好的sheet.
+ */
+ public void setXSSFValidation(Sheet sheet, String[] textlist, int firstRow, int endRow, int firstCol, int endCol)
+ {
+ DataValidationHelper helper = sheet.getDataValidationHelper();
+ // 加载下拉列表内容
+ DataValidationConstraint constraint = helper.createExplicitListConstraint(textlist);
+ // 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列
+ CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol);
+ // 数据有效性对象
+ DataValidation dataValidation = helper.createValidation(constraint, regions);
+ // 处理Excel兼容性问题
+ if (dataValidation instanceof XSSFDataValidation)
+ {
+ dataValidation.setSuppressDropDownArrow(true);
+ dataValidation.setShowErrorBox(true);
+ }
+ else
+ {
+ dataValidation.setSuppressDropDownArrow(false);
+ }
+
+ sheet.addValidationData(dataValidation);
+ }
+
+ /**
+ * 解析导出值 0=男,1=女,2=未知
+ *
+ * @param propertyValue 参数值
+ * @param converterExp 翻译注解
+ * @return 解析后值
+ * @throws Exception
+ */
+ public static String convertByExp(String propertyValue, String converterExp) throws Exception
+ {
+ try
+ {
+ String[] convertSource = converterExp.split(",");
+ for (String item : convertSource)
+ {
+ String[] itemArray = item.split("=");
+ if (itemArray[0].equals(propertyValue))
+ {
+ return itemArray[1];
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ throw e;
+ }
+ return propertyValue;
+ }
+
+ /**
+ * 反向解析值 男=0,女=1,未知=2
+ *
+ * @param propertyValue 参数值
+ * @param converterExp 翻译注解
+ * @return 解析后值
+ * @throws Exception
+ */
+ public static String reverseByExp(String propertyValue, String converterExp) throws Exception
+ {
+ try
+ {
+ String[] convertSource = converterExp.split(",");
+ for (String item : convertSource)
+ {
+ String[] itemArray = item.split("=");
+ if (itemArray[1].equals(propertyValue))
+ {
+ return itemArray[0];
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ throw e;
+ }
+ return propertyValue;
+ }
+
+ /**
+ * 获取bean中的属性值
+ *
+ * @param vo 实体对象
+ * @param field 字段
+ * @param excel 注解
+ * @return 最终的属性值
+ * @throws Exception
+ */
+ private Object getTargetValue(T vo, Field field, Excel excel) throws Exception
+ {
+ Object o = field.get(vo);
+ if (StringUtils.isNotEmpty(excel.targetAttr()))
+ {
+ String target = excel.targetAttr();
+ if (target.indexOf(".") > -1)
+ {
+ String[] targets = target.split("[.]");
+ for (String name : targets)
+ {
+ o = getValue(o, name);
+ }
+ }
+ else
+ {
+ o = getValue(o, target);
+ }
+ }
+ return o;
+ }
+
+ /**
+ * 以类的属性的get方法方法形式获取值
+ *
+ * @param o
+ * @param name
+ * @return value
+ * @throws Exception
+ */
+ private Object getValue(Object o, String name) throws Exception
+ {
+ if (StringUtils.isNotEmpty(name))
+ {
+ Class<?> clazz = o.getClass();
+ String methodName = "get" + name.substring(0, 1).toUpperCase() + name.substring(1);
+ Method method = clazz.getMethod(methodName);
+ o = method.invoke(o);
+ }
+ return o;
+ }
+
+ /**
+ * 得到所有定义字段
+ */
+ private void createExcelField()
+ {
+ this.fields = new ArrayList<Object[]>();
+ List<Field> tempFields = new ArrayList<>();
+ tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields()));
+ tempFields.addAll(Arrays.asList(clazz.getDeclaredFields()));
+ for (Field field : tempFields)
+ {
+ // 单注解
+ if (field.isAnnotationPresent(Excel.class))
+ {
+ putToField(field, field.getAnnotation(Excel.class));
+ }
+
+ // 多注解
+ if (field.isAnnotationPresent(Excels.class))
+ {
+ Excels attrs = field.getAnnotation(Excels.class);
+ Excel[] excels = attrs.value();
+ for (Excel excel : excels)
+ {
+ putToField(field, excel);
+ }
+ }
+ }
+ }
+
+ /**
+ * 放到字段集合中
+ */
+ private void putToField(Field field, Excel attr)
+ {
+ if (attr != null && (attr.type() == Type.ALL || attr.type() == type))
+ {
+ this.fields.add(new Object[] { field, attr });
+ }
+ }
+
+ /**
+ * 创建一个工作簿
+ */
+ public void createWorkbook()
+ {
+ this.wb = new SXSSFWorkbook(500);
+ }
+
+ /**
+ * 创建工作表
+ *
+ * @param sheetNo sheet数量
+ * @param index 序号
+ */
+ public void createSheet(double sheetNo, int index)
+ {
+ this.sheet = wb.createSheet();
+ this.styles = createStyles(wb);
+ // 设置工作表的名称.
+ if (sheetNo == 0)
+ {
+ wb.setSheetName(index, sheetName);
+ }
+ else
+ {
+ wb.setSheetName(index, sheetName + index);
+ }
+ }
+
+ /**
+ * 获取单元格值
+ *
+ * @param row 获取的行
+ * @param column 获取单元格列号
+ * @return 单元格值
+ */
+ public Object getCellValue(Row row, int column)
+ {
+ if (row == null)
+ {
+ return row;
+ }
+ Object val = "";
+ try
+ {
+ Cell cell = row.getCell(column);
+ if (StringUtils.isNotNull(cell))
+ {
+ if (cell.getCellTypeEnum() == CellType.NUMERIC || cell.getCellTypeEnum() == CellType.FORMULA)
+ {
+ val = cell.getNumericCellValue();
+ if (HSSFDateUtil.isCellDateFormatted(cell))
+ {
+ val = DateUtil.getJavaDate((Double) val); // POI Excel 日期格式转换
+ }
+ else
+ {
+ if ((Double) val % 1 > 0)
+ {
+ val = new DecimalFormat("0.00").format(val);
+ }
+ else
+ {
+ val = new DecimalFormat("0").format(val);
+ }
+ }
+ }
+ else if (cell.getCellTypeEnum() == CellType.STRING)
+ {
+ val = cell.getStringCellValue();
+ }
+ else if (cell.getCellTypeEnum() == CellType.BOOLEAN)
+ {
+ val = cell.getBooleanCellValue();
+ }
+ else if (cell.getCellTypeEnum() == CellType.ERROR)
+ {
+ val = cell.getErrorCellValue();
+ }
+
+ }
+ }
+ catch (Exception e)
+ {
+ return val;
+ }
+ return val;
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/reflect/ReflectUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/reflect/ReflectUtils.java
new file mode 100644
index 0000000..ee27ae0
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/reflect/ReflectUtils.java
@@ -0,0 +1,406 @@
+package com.ruoyi.common.core.utils.reflect;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Date;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.Validate;
+import org.apache.poi.ss.usermodel.DateUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import com.ruoyi.common.core.text.Convert;
+import com.ruoyi.common.core.utils.DateUtils;
+
+/**
+ * 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数.
+ *
+ * @author ruoyi
+ */
+@SuppressWarnings("rawtypes")
+public class ReflectUtils
+{
+ private static final String SETTER_PREFIX = "set";
+
+ private static final String GETTER_PREFIX = "get";
+
+ private static final String CGLIB_CLASS_SEPARATOR = "$$";
+
+ private static Logger logger = LoggerFactory.getLogger(ReflectUtils.class);
+
+ /**
+ * 调用Getter方法.
+ * 支持多级,如:对象名.对象名.方法
+ */
+ @SuppressWarnings("unchecked")
+ public static <E> E invokeGetter(Object obj, String propertyName)
+ {
+ Object object = obj;
+ for (String name : StringUtils.split(propertyName, "."))
+ {
+ String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name);
+ object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {});
+ }
+ return (E) object;
+ }
+
+ /**
+ * 调用Setter方法, 仅匹配方法名。
+ * 支持多级,如:对象名.对象名.方法
+ */
+ public static <E> void invokeSetter(Object obj, String propertyName, E value)
+ {
+ Object object = obj;
+ String[] names = StringUtils.split(propertyName, ".");
+ for (int i = 0; i < names.length; i++)
+ {
+ if (i < names.length - 1)
+ {
+ String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]);
+ object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {});
+ }
+ else
+ {
+ String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]);
+ invokeMethodByName(object, setterMethodName, new Object[] { value });
+ }
+ }
+ }
+
+ /**
+ * 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数.
+ */
+ @SuppressWarnings("unchecked")
+ public static <E> E getFieldValue(final Object obj, final String fieldName)
+ {
+ Field field = getAccessibleField(obj, fieldName);
+ if (field == null)
+ {
+ logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
+ return null;
+ }
+ E result = null;
+ try
+ {
+ result = (E) field.get(obj);
+ }
+ catch (IllegalAccessException e)
+ {
+ logger.error("不可能抛出的异常{}", e.getMessage());
+ }
+ return result;
+ }
+
+ /**
+ * 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数.
+ */
+ public static <E> void setFieldValue(final Object obj, final String fieldName, final E value)
+ {
+ Field field = getAccessibleField(obj, fieldName);
+ if (field == null)
+ {
+ // throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
+ logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
+ return;
+ }
+ try
+ {
+ field.set(obj, value);
+ }
+ catch (IllegalAccessException e)
+ {
+ logger.error("不可能抛出的异常: {}", e.getMessage());
+ }
+ }
+
+ /**
+ * 直接调用对象方法, 无视private/protected修饰符.
+ * 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用.
+ * 同时匹配方法名+参数类型,
+ */
+ @SuppressWarnings("unchecked")
+ public static <E> E invokeMethod(final Object obj, final String methodName, final Class<?>[] parameterTypes,
+ final Object[] args)
+ {
+ if (obj == null || methodName == null)
+ {
+ return null;
+ }
+ Method method = getAccessibleMethod(obj, methodName, parameterTypes);
+ if (method == null)
+ {
+ logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 ");
+ return null;
+ }
+ try
+ {
+ return (E) method.invoke(obj, args);
+ }
+ catch (Exception e)
+ {
+ String msg = "method: " + method + ", obj: " + obj + ", args: " + args + "";
+ throw convertReflectionExceptionToUnchecked(msg, e);
+ }
+ }
+
+ /**
+ * 直接调用对象方法, 无视private/protected修饰符,
+ * 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用.
+ * 只匹配函数名,如果有多个同名函数调用第一个。
+ */
+ @SuppressWarnings("unchecked")
+ public static <E> E invokeMethodByName(final Object obj, final String methodName, final Object[] args)
+ {
+ Method method = getAccessibleMethodByName(obj, methodName, args.length);
+ if (method == null)
+ {
+ // 如果为空不报错,直接返回空。
+ logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 ");
+ return null;
+ }
+ try
+ {
+ // 类型转换(将参数数据类型转换为目标方法参数类型)
+ Class<?>[] cs = method.getParameterTypes();
+ for (int i = 0; i < cs.length; i++)
+ {
+ if (args[i] != null && !args[i].getClass().equals(cs[i]))
+ {
+ if (cs[i] == String.class)
+ {
+ args[i] = Convert.toStr(args[i]);
+ if (StringUtils.endsWith((String) args[i], ".0"))
+ {
+ args[i] = StringUtils.substringBefore((String) args[i], ".0");
+ }
+ }
+ else if (cs[i] == Integer.class)
+ {
+ args[i] = Convert.toInt(args[i]);
+ }
+ else if (cs[i] == Long.class)
+ {
+ args[i] = Convert.toLong(args[i]);
+ }
+ else if (cs[i] == Double.class)
+ {
+ args[i] = Convert.toDouble(args[i]);
+ }
+ else if (cs[i] == Float.class)
+ {
+ args[i] = Convert.toFloat(args[i]);
+ }
+ else if (cs[i] == Date.class)
+ {
+ if (args[i] instanceof String)
+ {
+ args[i] = DateUtils.parseDate(args[i]);
+ }
+ else
+ {
+ args[i] = DateUtil.getJavaDate((Double) args[i]);
+ }
+ }
+ }
+ }
+ return (E) method.invoke(obj, args);
+ }
+ catch (Exception e)
+ {
+ String msg = "method: " + method + ", obj: " + obj + ", args: " + args + "";
+ throw convertReflectionExceptionToUnchecked(msg, e);
+ }
+ }
+
+ /**
+ * 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问.
+ * 如向上转型到Object仍无法找到, 返回null.
+ */
+ public static Field getAccessibleField(final Object obj, final String fieldName)
+ {
+ // 为空不报错。直接返回 null
+ if (obj == null)
+ {
+ return null;
+ }
+ Validate.notBlank(fieldName, "fieldName can't be blank");
+ for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass())
+ {
+ try
+ {
+ Field field = superClass.getDeclaredField(fieldName);
+ makeAccessible(field);
+ return field;
+ }
+ catch (NoSuchFieldException e)
+ {
+ continue;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
+ * 如向上转型到Object仍无法找到, 返回null.
+ * 匹配函数名+参数类型。
+ * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
+ */
+ public static Method getAccessibleMethod(final Object obj, final String methodName,
+ final Class<?>... parameterTypes)
+ {
+ // 为空不报错。直接返回 null
+ if (obj == null)
+ {
+ return null;
+ }
+ Validate.notBlank(methodName, "methodName can't be blank");
+ for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass())
+ {
+ try
+ {
+ Method method = searchType.getDeclaredMethod(methodName, parameterTypes);
+ makeAccessible(method);
+ return method;
+ }
+ catch (NoSuchMethodException e)
+ {
+ continue;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
+ * 如向上转型到Object仍无法找到, 返回null.
+ * 只匹配函数名。
+ * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
+ */
+ public static Method getAccessibleMethodByName(final Object obj, final String methodName, int argsNum)
+ {
+ // 为空不报错。直接返回 null
+ if (obj == null)
+ {
+ return null;
+ }
+ Validate.notBlank(methodName, "methodName can't be blank");
+ for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass())
+ {
+ Method[] methods = searchType.getDeclaredMethods();
+ for (Method method : methods)
+ {
+ if (method.getName().equals(methodName) && method.getParameterTypes().length == argsNum)
+ {
+ makeAccessible(method);
+ return method;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
+ */
+ public static void makeAccessible(Method method)
+ {
+ if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))
+ && !method.isAccessible())
+ {
+ method.setAccessible(true);
+ }
+ }
+
+ /**
+ * 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
+ */
+ public static void makeAccessible(Field field)
+ {
+ if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers())
+ || Modifier.isFinal(field.getModifiers())) && !field.isAccessible())
+ {
+ field.setAccessible(true);
+ }
+ }
+
+ /**
+ * 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处
+ * 如无法找到, 返回Object.class.
+ */
+ @SuppressWarnings("unchecked")
+ public static <T> Class<T> getClassGenricType(final Class clazz)
+ {
+ return getClassGenricType(clazz, 0);
+ }
+
+ /**
+ * 通过反射, 获得Class定义中声明的父类的泛型参数的类型.
+ * 如无法找到, 返回Object.class.
+ */
+ public static Class getClassGenricType(final Class clazz, final int index)
+ {
+ Type genType = clazz.getGenericSuperclass();
+
+ if (!(genType instanceof ParameterizedType))
+ {
+ logger.debug(clazz.getSimpleName() + "'s superclass not ParameterizedType");
+ return Object.class;
+ }
+
+ Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
+
+ if (index >= params.length || index < 0)
+ {
+ logger.debug("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: "
+ + params.length);
+ return Object.class;
+ }
+ if (!(params[index] instanceof Class))
+ {
+ logger.debug(clazz.getSimpleName() + " not set the actual class on superclass generic parameter");
+ return Object.class;
+ }
+
+ return (Class) params[index];
+ }
+
+ public static Class<?> getUserClass(Object instance)
+ {
+ if (instance == null)
+ {
+ throw new RuntimeException("Instance must not be null");
+ }
+ Class clazz = instance.getClass();
+ if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR))
+ {
+ Class<?> superClass = clazz.getSuperclass();
+ if (superClass != null && !Object.class.equals(superClass))
+ {
+ return superClass;
+ }
+ }
+ return clazz;
+
+ }
+
+ /**
+ * 将反射时的checked exception转换为unchecked exception.
+ */
+ public static RuntimeException convertReflectionExceptionToUnchecked(String msg, Exception e)
+ {
+ if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException
+ || e instanceof NoSuchMethodException)
+ {
+ return new IllegalArgumentException(msg, e);
+ }
+ else if (e instanceof InvocationTargetException)
+ {
+ return new RuntimeException(msg, ((InvocationTargetException) e).getTargetException());
+ }
+ return new RuntimeException(msg, e);
+ }
+}
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/sign/Base64.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/sign/Base64.java
new file mode 100644
index 0000000..6f6e8d3
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/sign/Base64.java
@@ -0,0 +1,291 @@
+package com.ruoyi.common.core.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-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/sql/SqlUtil.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/sql/SqlUtil.java
new file mode 100644
index 0000000..25f842e
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/sql/SqlUtil.java
@@ -0,0 +1,36 @@
+package com.ruoyi.common.core.utils.sql;
+
+import com.ruoyi.common.core.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-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/controller/BaseController.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/controller/BaseController.java
new file mode 100644
index 0000000..a4e4643
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/controller/BaseController.java
@@ -0,0 +1,87 @@
+package com.ruoyi.common.core.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.core.constant.HttpStatus;
+import com.ruoyi.common.core.utils.DateUtils;
+import com.ruoyi.common.core.utils.StringUtils;
+import com.ruoyi.common.core.utils.sql.SqlUtil;
+import com.ruoyi.common.core.web.domain.AjaxResult;
+import com.ruoyi.common.core.web.page.PageDomain;
+import com.ruoyi.common.core.web.page.TableDataInfo;
+import com.ruoyi.common.core.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-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/domain/AjaxResult.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/domain/AjaxResult.java
new file mode 100644
index 0000000..022aede
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/domain/AjaxResult.java
@@ -0,0 +1,148 @@
+package com.ruoyi.common.core.web.domain;
+
+import java.util.HashMap;
+import com.ruoyi.common.core.constant.HttpStatus;
+import com.ruoyi.common.core.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-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/domain/BaseEntity.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/domain/BaseEntity.java
new file mode 100644
index 0000000..ec15157
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/domain/BaseEntity.java
@@ -0,0 +1,156 @@
+package com.ruoyi.common.core.web.domain;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+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 beginTime;
+
+ /** 结束时间 */
+ @JsonIgnore
+ private String endTime;
+
+ /** 请求参数 */
+ private Map<String, Object> 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;
+ }
+
+ public String getBeginTime()
+ {
+ return beginTime;
+ }
+
+ public void setBeginTime(String beginTime)
+ {
+ this.beginTime = beginTime;
+ }
+
+ public String getEndTime()
+ {
+ return endTime;
+ }
+
+ public void setEndTime(String endTime)
+ {
+ this.endTime = endTime;
+ }
+
+ public Map<String, Object> getParams()
+ {
+ if (params == null)
+ {
+ params = new HashMap<>();
+ }
+ return params;
+ }
+
+ public void setParams(Map<String, Object> params)
+ {
+ this.params = params;
+ }
+}
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/page/PageDomain.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/page/PageDomain.java
new file mode 100644
index 0000000..367efa6
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/page/PageDomain.java
@@ -0,0 +1,72 @@
+package com.ruoyi.common.core.web.page;
+
+import com.ruoyi.common.core.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-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/page/TableDataInfo.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/page/TableDataInfo.java
new file mode 100644
index 0000000..41dc0fe
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/page/TableDataInfo.java
@@ -0,0 +1,85 @@
+package com.ruoyi.common.core.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 String 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 String getMsg()
+ {
+ return msg;
+ }
+
+ public void setMsg(String msg)
+ {
+ this.msg = msg;
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/page/TableSupport.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/page/TableSupport.java
new file mode 100644
index 0000000..351064e
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/page/TableSupport.java
@@ -0,0 +1,49 @@
+package com.ruoyi.common.core.web.page;
+
+import com.ruoyi.common.core.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-common/ruoyi-common-core/src/main/resources/META-INF/spring.factories b/ruoyi-common/ruoyi-common-core/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000..2628f32
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,4 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+ com.ruoyi.common.core.utils.SpringUtils
+
+
diff --git a/ruoyi-common/ruoyi-common-datascope/pom.xml b/ruoyi-common/ruoyi-common-datascope/pom.xml
new file mode 100644
index 0000000..b318f59
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-datascope/pom.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns="http://maven.apache.org/POM/4.0.0"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-common</artifactId>
+ <version>1.0.0</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>ruoyi-common-datascope</artifactId>
+
+ <description>
+ ruoyi-common-datascope权限范围
+ </description>
+
+ <dependencies>
+
+ <!-- RuoYi Common Security -->
+ <dependency>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-common-security</artifactId>
+ </dependency>
+
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/ruoyi-common/ruoyi-common-datascope/src/main/java/com/ruoyi/common/datascope/annotation/DataScope.java b/ruoyi-common/ruoyi-common-datascope/src/main/java/com/ruoyi/common/datascope/annotation/DataScope.java
new file mode 100644
index 0000000..de78297
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-datascope/src/main/java/com/ruoyi/common/datascope/annotation/DataScope.java
@@ -0,0 +1,28 @@
+package com.ruoyi.common.datascope.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-common/ruoyi-common-datascope/src/main/java/com/ruoyi/common/datascope/aspect/DataScopeAspect.java b/ruoyi-common/ruoyi-common-datascope/src/main/java/com/ruoyi/common/datascope/aspect/DataScopeAspect.java
new file mode 100644
index 0000000..8567e0d
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-datascope/src/main/java/com/ruoyi/common/datascope/aspect/DataScopeAspect.java
@@ -0,0 +1,162 @@
+package com.ruoyi.common.datascope.aspect;
+
+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.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import com.ruoyi.common.core.utils.StringUtils;
+import com.ruoyi.common.core.web.domain.BaseEntity;
+import com.ruoyi.common.datascope.annotation.DataScope;
+import com.ruoyi.common.datascope.service.AwaitUserService;
+import com.ruoyi.system.api.domain.SysRole;
+import com.ruoyi.system.api.domain.SysUser;
+import com.ruoyi.system.api.model.UserInfo;
+
+/**
+ * 数据过滤处理
+ *
+ * @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";
+
+ @Autowired
+ private AwaitUserService awaitUserService;
+
+ // 配置织入点
+ @Pointcut("@annotation(com.ruoyi.common.datascope.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;
+ }
+ // 获取当前的用户
+ UserInfo loginUser = awaitUserService.info();
+ SysUser currentUser = loginUser.getSysUser();
+ 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-common/ruoyi-common-datascope/src/main/java/com/ruoyi/common/datascope/service/AwaitUserService.java b/ruoyi-common/ruoyi-common-datascope/src/main/java/com/ruoyi/common/datascope/service/AwaitUserService.java
new file mode 100644
index 0000000..446b3f2
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-datascope/src/main/java/com/ruoyi/common/datascope/service/AwaitUserService.java
@@ -0,0 +1,42 @@
+package com.ruoyi.common.datascope.service;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.common.core.utils.StringUtils;
+import com.ruoyi.common.security.utils.SecurityUtils;
+import com.ruoyi.system.api.RemoteUserService;
+import com.ruoyi.system.api.model.UserInfo;
+
+/**
+ * 同步调用用户服务
+ *
+ * @author ruoyi
+ */
+@Service
+public class AwaitUserService
+{
+ private static final Logger log = LoggerFactory.getLogger(AwaitUserService.class);
+
+ @Autowired
+ private RemoteUserService remoteUserService;
+
+ /**
+ * 查询当前用户信息
+ *
+ * @return 用户基本信息
+ */
+ public UserInfo info()
+ {
+ String username = SecurityUtils.getUsername();
+ R<UserInfo> userResult = remoteUserService.getUserInfo(username);
+ if (StringUtils.isNull(userResult) || StringUtils.isNull(userResult.getData()))
+ {
+ log.info("数据权限范围查询用户:{} 不存在.", username);
+ return null;
+ }
+ return userResult.getData();
+ }
+}
diff --git a/ruoyi-common/ruoyi-common-datascope/src/main/resources/META-INF/spring.factories b/ruoyi-common/ruoyi-common-datascope/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000..52508bd
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-datascope/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,5 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+ com.ruoyi.common.datascope.service.AwaitUserService,\
+ com.ruoyi.common.datascope.aspect.DataScopeAspect
+
+
diff --git a/ruoyi-common/ruoyi-common-log/pom.xml b/ruoyi-common/ruoyi-common-log/pom.xml
new file mode 100644
index 0000000..a3b7977
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-log/pom.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns="http://maven.apache.org/POM/4.0.0"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-common</artifactId>
+ <version>1.0.0</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>ruoyi-common-log</artifactId>
+
+ <description>
+ ruoyi-common-log日志记录
+ </description>
+
+ <dependencies>
+
+ <!-- RuoYi Common Security -->
+ <dependency>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-common-security</artifactId>
+ </dependency>
+
+ <!-- Bitwalker -->
+ <dependency>
+ <groupId>eu.bitwalker</groupId>
+ <artifactId>UserAgentUtils</artifactId>
+ </dependency>
+
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/annotation/Log.java b/ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/annotation/Log.java
new file mode 100644
index 0000000..8cb5c18
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/annotation/Log.java
@@ -0,0 +1,41 @@
+package com.ruoyi.common.log.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.common.log.enums.BusinessType;
+import com.ruoyi.common.log.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-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/aspect/LogAspect.java b/ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/aspect/LogAspect.java
new file mode 100644
index 0000000..17fbe1e
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/aspect/LogAspect.java
@@ -0,0 +1,219 @@
+package com.ruoyi.common.log.aspect;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+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.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpMethod;
+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.core.utils.ServletUtils;
+import com.ruoyi.common.core.utils.StringUtils;
+import com.ruoyi.common.core.utils.ip.IpUtils;
+import com.ruoyi.common.log.annotation.Log;
+import com.ruoyi.common.log.enums.BusinessStatus;
+import com.ruoyi.common.log.service.AsyncLogService;
+import com.ruoyi.common.security.domain.LoginUser;
+import com.ruoyi.common.security.utils.SecurityUtils;
+import com.ruoyi.system.api.domain.SysOperLog;
+
+/**
+ * 操作日志记录处理
+ *
+ * @author ruoyi
+ */
+@Aspect
+@Component
+public class LogAspect
+{
+ private static final Logger log = LoggerFactory.getLogger(LogAspect.class);
+
+ @Autowired
+ private AsyncLogService asyncLogService;
+
+ // 配置织入点
+ @Pointcut("@annotation(com.ruoyi.common.log.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 = SecurityUtils.getLoginUser();
+
+ // *========数据库日志=========*//
+ 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);
+ // 保存数据库
+ asyncLogService.saveSysLog(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 (!isFilterObject(paramsArray[i]))
+ {
+ Object jsonObj = JSON.toJSON(paramsArray[i]);
+ params += jsonObj.toString() + " ";
+ }
+ }
+ }
+ return params.trim();
+ }
+
+ /**
+ * 判断是否需要过滤的对象。
+ *
+ * @param o 对象信息。
+ * @return 如果是需要过滤的对象,则返回true;否则返回false。
+ */
+ public boolean isFilterObject(final Object o)
+ {
+ return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse;
+ }
+}
diff --git a/ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/enums/BusinessStatus.java b/ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/enums/BusinessStatus.java
new file mode 100644
index 0000000..cd644ba
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/enums/BusinessStatus.java
@@ -0,0 +1,20 @@
+package com.ruoyi.common.log.enums;
+
+/**
+ * 操作状态
+ *
+ * @author ruoyi
+ *
+ */
+public enum BusinessStatus
+{
+ /**
+ * 成功
+ */
+ SUCCESS,
+
+ /**
+ * 失败
+ */
+ FAIL,
+}
diff --git a/ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/enums/BusinessType.java b/ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/enums/BusinessType.java
new file mode 100644
index 0000000..3a9a71c
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/enums/BusinessType.java
@@ -0,0 +1,59 @@
+package com.ruoyi.common.log.enums;
+
+/**
+ * 业务操作类型
+ *
+ * @author ruoyi
+ */
+public enum BusinessType
+{
+ /**
+ * 其它
+ */
+ OTHER,
+
+ /**
+ * 新增
+ */
+ INSERT,
+
+ /**
+ * 修改
+ */
+ UPDATE,
+
+ /**
+ * 删除
+ */
+ DELETE,
+
+ /**
+ * 授权
+ */
+ GRANT,
+
+ /**
+ * 导出
+ */
+ EXPORT,
+
+ /**
+ * 导入
+ */
+ IMPORT,
+
+ /**
+ * 强退
+ */
+ FORCE,
+
+ /**
+ * 生成代码
+ */
+ GENCODE,
+
+ /**
+ * 清空数据
+ */
+ CLEAN,
+}
diff --git a/ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/enums/OperatorType.java b/ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/enums/OperatorType.java
new file mode 100644
index 0000000..b122f25
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/enums/OperatorType.java
@@ -0,0 +1,24 @@
+package com.ruoyi.common.log.enums;
+
+/**
+ * 操作人类别
+ *
+ * @author ruoyi
+ */
+public enum OperatorType
+{
+ /**
+ * 其它
+ */
+ OTHER,
+
+ /**
+ * 后台用户
+ */
+ MANAGE,
+
+ /**
+ * 手机端用户
+ */
+ MOBILE
+}
diff --git a/ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/service/AsyncLogService.java b/ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/service/AsyncLogService.java
new file mode 100644
index 0000000..c4ea247
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-log/src/main/java/com/ruoyi/common/log/service/AsyncLogService.java
@@ -0,0 +1,28 @@
+package com.ruoyi.common.log.service;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+import com.ruoyi.system.api.RemoteLogService;
+import com.ruoyi.system.api.domain.SysOperLog;
+
+/**
+ * 异步调用日志服务
+ *
+ * @author ruoyi
+ */
+@Service
+public class AsyncLogService
+{
+ @Autowired
+ private RemoteLogService remoteLogService;
+
+ /**
+ * 保存系统日志记录
+ */
+ @Async
+ public void saveSysLog(SysOperLog sysOperLog)
+ {
+ remoteLogService.saveLog(sysOperLog);
+ }
+}
diff --git a/ruoyi-common/ruoyi-common-log/src/main/resources/META-INF/spring.factories b/ruoyi-common/ruoyi-common-log/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000..4a5ae7d
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-log/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,3 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+ com.ruoyi.common.log.service.AsyncLogService,\
+ com.ruoyi.common.log.aspect.LogAspect
diff --git a/ruoyi-common/ruoyi-common-redis/pom.xml b/ruoyi-common/ruoyi-common-redis/pom.xml
new file mode 100644
index 0000000..407257b
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-redis/pom.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns="http://maven.apache.org/POM/4.0.0"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-common</artifactId>
+ <version>1.0.0</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>ruoyi-common-redis</artifactId>
+
+ <description>
+ ruoyi-common-redis缓存服务
+ </description>
+
+ <dependencies>
+
+ <!-- SpringBoot Boot Redis -->
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-data-redis</artifactId>
+ </dependency>
+
+ <!-- RuoYi Common Core-->
+ <dependency>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-common-core</artifactId>
+ </dependency>
+
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/configure/FastJson2JsonRedisSerializer.java b/ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/configure/FastJson2JsonRedisSerializer.java
new file mode 100644
index 0000000..597939c
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/configure/FastJson2JsonRedisSerializer.java
@@ -0,0 +1,69 @@
+package com.ruoyi.common.redis.configure;
+
+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-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/configure/RedisConfig.java b/ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/configure/RedisConfig.java
new file mode 100644
index 0000000..0499feb
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/configure/RedisConfig.java
@@ -0,0 +1,43 @@
+package com.ruoyi.common.redis.configure;
+
+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", "deprecation" })
+ 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-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/service/RedisService.java b/ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/service/RedisService.java
new file mode 100644
index 0000000..8bea5da
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-redis/src/main/java/com/ruoyi/common/redis/service/RedisService.java
@@ -0,0 +1,207 @@
+package com.ruoyi.common.redis.service;
+
+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 RedisService
+{
+ @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);
+ dataSet = operation.members();
+ 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;
+ }
+
+ /**
+ * 获得缓存的基本对象列表
+ *
+ * @param pattern 字符串前缀
+ * @return 对象列表
+ */
+ public Collection<String> keys(String pattern)
+ {
+ return redisTemplate.keys(pattern);
+ }
+}
diff --git a/ruoyi-common/ruoyi-common-redis/src/main/resources/META-INF/spring.factories b/ruoyi-common/ruoyi-common-redis/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000..2f752bf
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-redis/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,4 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+ com.ruoyi.common.redis.service.RedisService
+
+
diff --git a/ruoyi-common/ruoyi-common-security/pom.xml b/ruoyi-common/ruoyi-common-security/pom.xml
new file mode 100644
index 0000000..1a596a4
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-security/pom.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-common</artifactId>
+ <version>1.0.0</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>ruoyi-common-security</artifactId>
+
+ <description>
+ ruoyi-common-security安全模块
+ </description>
+
+ <dependencies>
+
+ <!-- Spring Security Oauth2 -->
+ <dependency>
+ <groupId>org.springframework.cloud</groupId>
+ <artifactId>spring-cloud-starter-oauth2</artifactId>
+ </dependency>
+
+ <!-- RuoYi Api System -->
+ <dependency>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-api-system</artifactId>
+ </dependency>
+
+ </dependencies>
+
+</project>
diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/annotation/EnableCustomConfig.java b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/annotation/EnableCustomConfig.java
new file mode 100644
index 0000000..2ca2ad0
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/annotation/EnableCustomConfig.java
@@ -0,0 +1,26 @@
+package com.ruoyi.common.security.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 org.mybatis.spring.annotation.MapperScan;
+import org.springframework.context.annotation.EnableAspectJAutoProxy;
+import org.springframework.scheduling.annotation.EnableAsync;
+
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+// 表示通过aop框架暴露该代理对象,AopContext能够访问
+@EnableAspectJAutoProxy(exposeProxy = true)
+// 指定要扫描的Mapper类的包的路径
+@MapperScan("com.ruoyi.**.mapper")
+// 开启线程异步执行
+@EnableAsync
+public @interface EnableCustomConfig
+{
+
+}
diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/annotation/EnableRyFeignClients.java b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/annotation/EnableRyFeignClients.java
new file mode 100644
index 0000000..066e834
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/annotation/EnableRyFeignClients.java
@@ -0,0 +1,27 @@
+package com.ruoyi.common.security.annotation;
+
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import java.lang.annotation.*;
+
+/**
+ * 自定义feign注解
+ * 添加basePackages路径
+ *
+ * @author ruoyi
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@EnableFeignClients
+public @interface EnableRyFeignClients
+{
+ String[] value() default {};
+
+ String[] basePackages() default { "com.ruoyi" };
+
+ Class<?>[] basePackageClasses() default {};
+
+ Class<?>[] defaultConfiguration() default {};
+
+ Class<?>[] clients() default {};
+}
diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/config/AuthIgnoreConfig.java b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/config/AuthIgnoreConfig.java
new file mode 100644
index 0000000..e858d4c
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/config/AuthIgnoreConfig.java
@@ -0,0 +1,28 @@
+package com.ruoyi.common.security.config;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.springframework.beans.factory.annotation.Configurable;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * 忽略服务间的认证
+ *
+ * @author ruoyi
+ **/
+@Configurable
+@ConfigurationProperties(prefix = "security.oauth2.ignore")
+public class AuthIgnoreConfig
+{
+ private List<String> urls = new ArrayList<>();
+
+ public List<String> getUrls()
+ {
+ return urls;
+ }
+
+ public void setUrls(List<String> urls)
+ {
+ this.urls = urls;
+ }
+}
diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/config/CommonUserConverter.java b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/config/CommonUserConverter.java
new file mode 100644
index 0000000..29f2afb
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/config/CommonUserConverter.java
@@ -0,0 +1,75 @@
+package com.ruoyi.common.security.config;
+
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.AuthorityUtils;
+import org.springframework.security.oauth2.provider.token.UserAuthenticationConverter;
+import org.springframework.util.StringUtils;
+import com.ruoyi.common.core.constant.SecurityConstants;
+import com.ruoyi.common.core.text.Convert;
+import com.ruoyi.common.security.domain.LoginUser;
+
+/**
+ * https://my.oschina.net/giegie/blog/3023768 根据checktoken 的结果转化用户信息
+ *
+ * @author lengleng
+ */
+public class CommonUserConverter implements UserAuthenticationConverter
+{
+ private static final String N_A = "N/A";
+
+ /**
+ * 将授权信息返回到资源服务
+ */
+ @Override
+ public Map<String, ?> convertUserAuthentication(Authentication userAuthentication)
+ {
+ Map<String, Object> authMap = new LinkedHashMap<>();
+ authMap.put(USERNAME, userAuthentication.getName());
+ if (userAuthentication.getAuthorities() != null && !userAuthentication.getAuthorities().isEmpty())
+ {
+ authMap.put(AUTHORITIES, AuthorityUtils.authorityListToSet(userAuthentication.getAuthorities()));
+ }
+ return authMap;
+ }
+
+ /**
+ * 获取用户认证信息
+ */
+ @Override
+ public Authentication extractAuthentication(Map<String, ?> map)
+ {
+ if (map.containsKey(USERNAME))
+ {
+ Collection<? extends GrantedAuthority> authorities = getAuthorities(map);
+
+ Long userId = Convert.toLong(map.get(SecurityConstants.DETAILS_USER_ID));
+ String username = (String) map.get(SecurityConstants.DETAILS_USERNAME);
+ LoginUser user = new LoginUser(userId, username, N_A, true, true, true, true, authorities);
+ return new UsernamePasswordAuthenticationToken(user, N_A, authorities);
+ }
+ return null;
+ }
+
+ /**
+ * 获取权限资源信息
+ */
+ private Collection<? extends GrantedAuthority> getAuthorities(Map<String, ?> map)
+ {
+ Object authorities = map.get(AUTHORITIES);
+ if (authorities instanceof String)
+ {
+ return AuthorityUtils.commaSeparatedStringToAuthorityList((String) authorities);
+ }
+ if (authorities instanceof Collection)
+ {
+ return AuthorityUtils.commaSeparatedStringToAuthorityList(
+ StringUtils.collectionToCommaDelimitedString((Collection<?>) authorities));
+ }
+ throw new IllegalArgumentException("Authorities must be either a String or a Collection");
+ }
+}
diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/config/ResourceServerConfig.java b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/config/ResourceServerConfig.java
new file mode 100644
index 0000000..6aa3ba8
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/config/ResourceServerConfig.java
@@ -0,0 +1,82 @@
+package com.ruoyi.common.security.config;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.security.oauth2.OAuth2ClientProperties;
+import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties;
+import org.springframework.cloud.client.loadbalancer.LoadBalanced;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
+import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
+import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
+import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
+import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter;
+import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
+import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
+import org.springframework.security.oauth2.provider.token.UserAuthenticationConverter;
+import org.springframework.web.client.DefaultResponseErrorHandler;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * oauth2 服务配置
+ *
+ * @author ruoyi
+ */
+@Configuration
+@EnableResourceServer
+public class ResourceServerConfig extends ResourceServerConfigurerAdapter
+{
+ @Autowired
+ private ResourceServerProperties resourceServerProperties;
+
+ @Autowired
+ private OAuth2ClientProperties oAuth2ClientProperties;
+
+ @Bean
+ public AuthIgnoreConfig authIgnoreConfig()
+ {
+ return new AuthIgnoreConfig();
+ }
+
+ @Bean
+ @LoadBalanced
+ public RestTemplate restTemplate()
+ {
+ RestTemplate restTemplate = new RestTemplate();
+ restTemplate.setErrorHandler(new DefaultResponseErrorHandler());
+ return restTemplate;
+ }
+
+ @Bean
+ public ResourceServerTokenServices tokenServices()
+ {
+ RemoteTokenServices remoteTokenServices = new RemoteTokenServices();
+ DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter();
+ UserAuthenticationConverter userTokenConverter = new CommonUserConverter();
+ accessTokenConverter.setUserTokenConverter(userTokenConverter);
+ remoteTokenServices.setCheckTokenEndpointUrl(resourceServerProperties.getTokenInfoUri());
+ remoteTokenServices.setClientId(oAuth2ClientProperties.getClientId());
+ remoteTokenServices.setClientSecret(oAuth2ClientProperties.getClientSecret());
+ remoteTokenServices.setRestTemplate(restTemplate());
+ remoteTokenServices.setAccessTokenConverter(accessTokenConverter);
+ return remoteTokenServices;
+ }
+
+ @Override
+ public void configure(HttpSecurity http) throws Exception
+ {
+ http.csrf().disable();
+ ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http
+ .authorizeRequests();
+ // 不登录可以访问
+ authIgnoreConfig().getUrls().forEach(url -> registry.antMatchers(url).permitAll());
+ registry.anyRequest().authenticated();
+ }
+
+ @Override
+ public void configure(ResourceServerSecurityConfigurer resources)
+ {
+ resources.tokenServices(tokenServices());
+ }
+}
diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/domain/LoginUser.java b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/domain/LoginUser.java
new file mode 100644
index 0000000..1056fec
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/domain/LoginUser.java
@@ -0,0 +1,37 @@
+package com.ruoyi.common.security.domain;
+
+import java.util.Collection;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.User;
+
+/**
+ * 登录用户身份权限
+ *
+ * @author ruoyi
+ */
+public class LoginUser extends User
+{
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 用户ID
+ */
+ private Long userId;
+
+ public LoginUser(Long userId, String username, String password, boolean enabled, boolean accountNonExpired,
+ boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities)
+ {
+ super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
+ this.userId = userId;
+ }
+
+ public Long getUserId()
+ {
+ return userId;
+ }
+
+ public void setUserId(Long userId)
+ {
+ this.userId = userId;
+ }
+}
diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/feign/OAuth2FeignConfig.java b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/feign/OAuth2FeignConfig.java
new file mode 100644
index 0000000..e5c598b
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/feign/OAuth2FeignConfig.java
@@ -0,0 +1,20 @@
+package com.ruoyi.common.security.feign;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import feign.RequestInterceptor;
+
+/**
+ * Feign配置注册
+ *
+ * @author ruoyi
+ **/
+@Configuration
+public class OAuth2FeignConfig
+{
+ @Bean
+ public RequestInterceptor requestInterceptor()
+ {
+ return new OAuth2FeignRequestInterceptor();
+ }
+}
diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/feign/OAuth2FeignRequestInterceptor.java b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/feign/OAuth2FeignRequestInterceptor.java
new file mode 100644
index 0000000..6009916
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/feign/OAuth2FeignRequestInterceptor.java
@@ -0,0 +1,35 @@
+package com.ruoyi.common.security.feign;
+
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
+import org.springframework.stereotype.Component;
+import feign.RequestInterceptor;
+import feign.RequestTemplate;
+
+/**
+ * feign 请求拦截器
+ *
+ * @author ruoyi
+ */
+@Component
+public class OAuth2FeignRequestInterceptor implements RequestInterceptor
+{
+ private final String AUTHORIZATION_HEADER = "Authorization";
+
+ private final String BEARER_TOKEN_TYPE = "Bearer";
+
+ @Override
+ public void apply(RequestTemplate requestTemplate)
+ {
+ SecurityContext securityContext = SecurityContextHolder.getContext();
+ Authentication authentication = securityContext.getAuthentication();
+ if (authentication != null && authentication.getDetails() instanceof OAuth2AuthenticationDetails)
+ {
+ OAuth2AuthenticationDetails dateils = (OAuth2AuthenticationDetails) authentication.getDetails();
+ requestTemplate.header(AUTHORIZATION_HEADER,
+ String.format("%s %s", BEARER_TOKEN_TYPE, dateils.getTokenValue()));
+ }
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/handler/CustomAccessDeniedHandler.java b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/handler/CustomAccessDeniedHandler.java
new file mode 100644
index 0000000..d5ba1be
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/handler/CustomAccessDeniedHandler.java
@@ -0,0 +1,33 @@
+package com.ruoyi.common.security.handler;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler;
+import org.springframework.stereotype.Component;
+import com.alibaba.fastjson.JSON;
+import com.ruoyi.common.core.constant.HttpStatus;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.common.core.utils.ServletUtils;
+
+/**
+ * 自定义访问无权限资源时的异常
+ *
+ * @author ruoyi
+ */
+@Component
+public class CustomAccessDeniedHandler extends OAuth2AccessDeniedHandler
+{
+ private final Logger logger = LoggerFactory.getLogger(CustomAccessDeniedHandler.class);
+
+ @Override
+ public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException authException)
+ {
+ logger.info("权限不足,请联系管理员 {}", request.getRequestURI());
+
+ String msg = authException.getMessage();
+ ServletUtils.renderString(response, JSON.toJSONString(R.failed(HttpStatus.FORBIDDEN, msg)));
+ }
+}
diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/PermissionService.java b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/PermissionService.java
new file mode 100644
index 0000000..967f567
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/PermissionService.java
@@ -0,0 +1,167 @@
+package com.ruoyi.common.security.service;
+
+import java.util.Collection;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.PatternMatchUtils;
+import org.springframework.util.StringUtils;
+import com.ruoyi.common.security.domain.LoginUser;
+import com.ruoyi.common.security.utils.SecurityUtils;
+
+/**
+ * 自定义权限实现
+ *
+ * @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 = ",";
+
+ /**
+ * 验证用户是否具备某权限
+ *
+ * @param permission 权限字符串
+ * @return 用户是否具备某权限
+ */
+ public boolean hasPermi(String permission)
+ {
+ if (StringUtils.isEmpty(permission))
+ {
+ return false;
+ }
+ LoginUser loginUser = SecurityUtils.getLoginUser();
+ if (StringUtils.isEmpty(loginUser) || CollectionUtils.isEmpty(loginUser.getAuthorities()))
+ {
+ return false;
+ }
+ return hasPermissions(loginUser.getAuthorities(), 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 = SecurityUtils.getLoginUser();
+ if (StringUtils.isEmpty(loginUser) || CollectionUtils.isEmpty(loginUser.getAuthorities()))
+ {
+ return false;
+ }
+ Collection<? extends GrantedAuthority> authorities = loginUser.getAuthorities();
+ 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 = SecurityUtils.getLoginUser();
+ if (StringUtils.isEmpty(loginUser) || CollectionUtils.isEmpty(loginUser.getAuthorities()))
+ {
+ return false;
+ }
+ for (GrantedAuthority authorities : loginUser.getAuthorities())
+ {
+ String roleKey = authorities.getAuthority();
+ if (SUPER_ADMIN.contains(roleKey) || roleKey.contains(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 = SecurityUtils.getLoginUser();
+ if (StringUtils.isEmpty(loginUser) || CollectionUtils.isEmpty(loginUser.getAuthorities()))
+ {
+ return false;
+ }
+ for (String role : roles.split(ROLE_DELIMETER))
+ {
+ if (hasRole(role))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 判断是否包含权限
+ *
+ * @param permissions 权限列表
+ * @param permission 权限字符串
+ * @return 用户是否具备某权限
+ */
+ private boolean hasPermissions(Collection<? extends GrantedAuthority> authorities, String permission)
+ {
+ return authorities.stream().map(GrantedAuthority::getAuthority).filter(StringUtils::hasText)
+ .anyMatch(x -> ALL_PERMISSION.contains(x) || PatternMatchUtils.simpleMatch(permission, x));
+ }
+}
diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/RedisClientDetailsService.java b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/RedisClientDetailsService.java
new file mode 100644
index 0000000..5f0700f
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/RedisClientDetailsService.java
@@ -0,0 +1,30 @@
+package com.ruoyi.common.security.service;
+
+import javax.sql.DataSource;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.security.oauth2.provider.ClientDetails;
+import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
+import com.ruoyi.common.core.constant.CacheConstants;
+import com.ruoyi.common.core.constant.SecurityConstants;
+
+/**
+ * 重写原生方法支持redis缓存
+ *
+ * @author ruoyi
+ */
+public class RedisClientDetailsService extends JdbcClientDetailsService
+{
+ public RedisClientDetailsService(DataSource dataSource)
+ {
+ super(dataSource);
+ super.setSelectClientDetailsSql(SecurityConstants.DEFAULT_SELECT_STATEMENT);
+ super.setFindClientDetailsSql(SecurityConstants.DEFAULT_FIND_STATEMENT);
+ }
+
+ @Override
+ @Cacheable(value = CacheConstants.CLIENT_DETAILS_KEY, key = "#clientId", unless = "#result == null")
+ public ClientDetails loadClientByClientId(String clientId)
+ {
+ return super.loadClientByClientId(clientId);
+ }
+}
diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/UserDetailsServiceImpl.java b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/UserDetailsServiceImpl.java
new file mode 100644
index 0000000..28abc4b
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/UserDetailsServiceImpl.java
@@ -0,0 +1,83 @@
+package com.ruoyi.common.security.service;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.AuthorityUtils;
+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.core.domain.R;
+import com.ruoyi.common.core.enums.UserStatus;
+import com.ruoyi.common.core.exception.BaseException;
+import com.ruoyi.common.core.utils.StringUtils;
+import com.ruoyi.common.security.domain.LoginUser;
+import com.ruoyi.system.api.RemoteUserService;
+import com.ruoyi.system.api.domain.SysUser;
+import com.ruoyi.system.api.model.UserInfo;
+
+/**
+ * 用户信息处理
+ *
+ * @author ruoyi
+ */
+@Service
+public class UserDetailsServiceImpl implements UserDetailsService
+{
+ private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class);
+
+ @Autowired
+ private RemoteUserService remoteUserService;
+
+ @Override
+ public UserDetails loadUserByUsername(String username)
+ {
+ R<UserInfo> userResult = remoteUserService.getUserInfo(username);
+ checkUser(userResult, username);
+ return getUserDetails(userResult);
+ }
+
+ public void checkUser(R<UserInfo> userResult, String username)
+ {
+ if (StringUtils.isNull(userResult) || StringUtils.isNull(userResult.getData()))
+ {
+ log.info("登录用户:{} 不存在.", username);
+ throw new UsernameNotFoundException("登录用户:" + username + " 不存在");
+ }
+ else if (UserStatus.DELETED.getCode().equals(userResult.getData().getSysUser().getDelFlag()))
+ {
+ log.info("登录用户:{} 已被删除.", username);
+ throw new BaseException("对不起,您的账号:" + username + " 已被删除");
+ }
+ else if (UserStatus.DISABLE.getCode().equals(userResult.getData().getSysUser().getStatus()))
+ {
+ log.info("登录用户:{} 已被停用.", username);
+ throw new BaseException("对不起,您的账号:" + username + " 已停用");
+ }
+ }
+
+ private UserDetails getUserDetails(R<UserInfo> result)
+ {
+ UserInfo info = result.getData();
+ Set<String> dbAuthsSet = new HashSet<String>();
+ if (StringUtils.isNotEmpty(info.getRoles()))
+ {
+ // 获取角色
+ dbAuthsSet.addAll(info.getRoles());
+ // 获取权限
+ dbAuthsSet.addAll(info.getPermissions());
+ }
+
+ Collection<? extends GrantedAuthority> authorities = AuthorityUtils
+ .createAuthorityList(dbAuthsSet.toArray(new String[0]));
+ SysUser user = info.getSysUser();
+
+ return new LoginUser(user.getUserId(), user.getUserName(), user.getPassword(), true, true, true, true,
+ authorities);
+ }
+}
diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/utils/SecurityUtils.java b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/utils/SecurityUtils.java
new file mode 100644
index 0000000..b0c1e8d
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/utils/SecurityUtils.java
@@ -0,0 +1,92 @@
+package com.ruoyi.common.security.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.security.domain.LoginUser;
+
+/**
+ * 权限获取工具类
+ *
+ * @author ruoyi
+ */
+public class SecurityUtils
+{
+ /**
+ * 获取Authentication
+ */
+ public static Authentication getAuthentication()
+ {
+ return SecurityContextHolder.getContext().getAuthentication();
+ }
+
+ /**
+ * 获取用户
+ */
+ public static String getUsername()
+ {
+ return getLoginUser().getUsername();
+ }
+
+ /**
+ * 获取用户
+ */
+ public static LoginUser getLoginUser(Authentication authentication)
+ {
+ Object principal = authentication.getPrincipal();
+ if (principal instanceof LoginUser)
+ {
+ return (LoginUser) principal;
+ }
+ return null;
+ }
+
+ /**
+ * 获取用户
+ */
+ public static LoginUser getLoginUser()
+ {
+ Authentication authentication = getAuthentication();
+ if (authentication == null)
+ {
+ return null;
+ }
+ return getLoginUser(authentication);
+ }
+
+ /**
+ * 生成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-common/ruoyi-common-security/src/main/resources/META-INF/spring.factories b/ruoyi-common/ruoyi-common-security/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000..14658c3
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-security/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,6 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+ com.ruoyi.common.security.service.UserDetailsServiceImpl,\
+ com.ruoyi.common.security.handler.CustomAccessDeniedHandler,\
+ com.ruoyi.common.security.config.ResourceServerConfig
+
+
diff --git a/ruoyi-common/ruoyi-common-swagger/pom.xml b/ruoyi-common/ruoyi-common-swagger/pom.xml
new file mode 100644
index 0000000..a9f231a
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-swagger/pom.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns="http://maven.apache.org/POM/4.0.0"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-common</artifactId>
+ <version>1.0.0</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>ruoyi-common-swagger</artifactId>
+
+ <description>
+ ruoyi-common-swagger系统接口
+ </description>
+
+ <dependencies>
+
+ <!-- SpringBoot Web -->
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-web</artifactId>
+ </dependency>
+
+ <!-- Swagger-->
+ <dependency>
+ <groupId>io.springfox</groupId>
+ <artifactId>springfox-swagger2</artifactId>
+ <version>${swagger.fox.version}</version>
+ </dependency>
+
+ </dependencies>
+</project>
diff --git a/ruoyi-common/ruoyi-common-swagger/src/main/java/com/ruoyi/common/swagger/annotation/EnableCustomSwagger2.java b/ruoyi-common/ruoyi-common-swagger/src/main/java/com/ruoyi/common/swagger/annotation/EnableCustomSwagger2.java
new file mode 100644
index 0000000..6832fe1
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-swagger/src/main/java/com/ruoyi/common/swagger/annotation/EnableCustomSwagger2.java
@@ -0,0 +1,20 @@
+package com.ruoyi.common.swagger.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 org.springframework.context.annotation.Import;
+import com.ruoyi.common.swagger.config.SwaggerAutoConfiguration;
+
+@Target({ ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+@Import({ SwaggerAutoConfiguration.class })
+public @interface EnableCustomSwagger2
+{
+
+}
diff --git a/ruoyi-common/ruoyi-common-swagger/src/main/java/com/ruoyi/common/swagger/config/SwaggerAutoConfiguration.java b/ruoyi-common/ruoyi-common-swagger/src/main/java/com/ruoyi/common/swagger/config/SwaggerAutoConfiguration.java
new file mode 100644
index 0000000..204c2c8
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-swagger/src/main/java/com/ruoyi/common/swagger/config/SwaggerAutoConfiguration.java
@@ -0,0 +1,132 @@
+package com.ruoyi.common.swagger.config;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.service.AuthorizationScope;
+import springfox.documentation.service.Contact;
+import springfox.documentation.service.GrantType;
+import springfox.documentation.service.OAuth;
+import springfox.documentation.service.ResourceOwnerPasswordCredentialsGrant;
+import springfox.documentation.service.SecurityReference;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spi.service.contexts.SecurityContext;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+@Configuration
+@EnableSwagger2
+@EnableAutoConfiguration
+@ConditionalOnProperty(name = "swagger.enabled", matchIfMissing = true)
+public class SwaggerAutoConfiguration
+{
+ /**
+ * 默认的排除路径,排除Spring Boot默认的错误处理路径和端点
+ */
+ private static final List<String> DEFAULT_EXCLUDE_PATH = Arrays.asList("/error", "/actuator/**");
+
+ private static final String BASE_PATH = "/**";
+
+ @Bean
+ @ConditionalOnMissingBean
+ public SwaggerProperties swaggerProperties()
+ {
+ return new SwaggerProperties();
+ }
+
+ @Bean
+ public Docket api(SwaggerProperties swaggerProperties)
+ {
+ // base-path处理
+ if (swaggerProperties.getBasePath().isEmpty())
+ {
+ swaggerProperties.getBasePath().add(BASE_PATH);
+ }
+ // noinspection unchecked
+ List<Predicate<String>> basePath = new ArrayList<Predicate<String>>();
+ swaggerProperties.getBasePath().forEach(path -> basePath.add(PathSelectors.ant(path)));
+
+ // exclude-path处理
+ if (swaggerProperties.getExcludePath().isEmpty())
+ {
+ swaggerProperties.getExcludePath().addAll(DEFAULT_EXCLUDE_PATH);
+ }
+ List<Predicate<String>> excludePath = new ArrayList<>();
+ swaggerProperties.getExcludePath().forEach(path -> excludePath.add(PathSelectors.ant(path)));
+
+ //noinspection Guava
+ return new Docket(DocumentationType.SWAGGER_2)
+ .host(swaggerProperties.getHost())
+ .apiInfo(apiInfo(swaggerProperties)).select()
+ .apis(RequestHandlerSelectors.basePackage(swaggerProperties.getBasePackage()))
+ .paths(Predicates.and(Predicates.not(Predicates.or(excludePath)), Predicates.or(basePath)))
+ .build()
+ .securitySchemes(Collections.singletonList(securitySchema()))
+ .securityContexts(Collections.singletonList(securityContext()))
+ .pathMapping("/");
+ }
+
+ /**
+ * 配置默认的全局鉴权策略的开关,通过正则表达式进行匹配;默认匹配所有URL
+ *
+ * @return
+ */
+ private SecurityContext securityContext()
+ {
+ return SecurityContext.builder()
+ .securityReferences(defaultAuth())
+ .forPaths(PathSelectors.regex(swaggerProperties().getAuthorization().getAuthRegex()))
+ .build();
+ }
+
+ /**
+ * 默认的全局鉴权策略
+ *
+ * @return
+ */
+ private List<SecurityReference> defaultAuth()
+ {
+ ArrayList<AuthorizationScope> authorizationScopeList = new ArrayList<>();
+ swaggerProperties().getAuthorization().getAuthorizationScopeList().forEach(authorizationScope -> authorizationScopeList.add(new AuthorizationScope(authorizationScope.getScope(), authorizationScope.getDescription())));
+ AuthorizationScope[] authorizationScopes = new AuthorizationScope[authorizationScopeList.size()];
+ return Collections.singletonList(SecurityReference.builder()
+ .reference(swaggerProperties().getAuthorization().getName())
+ .scopes(authorizationScopeList.toArray(authorizationScopes))
+ .build());
+ }
+
+ private OAuth securitySchema()
+ {
+ ArrayList<AuthorizationScope> authorizationScopeList = new ArrayList<>();
+ swaggerProperties().getAuthorization().getAuthorizationScopeList().forEach(authorizationScope -> authorizationScopeList.add(new AuthorizationScope(authorizationScope.getScope(), authorizationScope.getDescription())));
+ ArrayList<GrantType> grantTypes = new ArrayList<>();
+ swaggerProperties().getAuthorization().getTokenUrlList().forEach(tokenUrl -> grantTypes.add(new ResourceOwnerPasswordCredentialsGrant(tokenUrl)));
+ return new OAuth(swaggerProperties().getAuthorization().getName(), authorizationScopeList, grantTypes);
+ }
+
+ private ApiInfo apiInfo(SwaggerProperties swaggerProperties)
+ {
+ return new ApiInfoBuilder()
+ .title(swaggerProperties.getTitle())
+ .description(swaggerProperties.getDescription())
+ .license(swaggerProperties.getLicense())
+ .licenseUrl(swaggerProperties.getLicenseUrl())
+ .termsOfServiceUrl(swaggerProperties.getTermsOfServiceUrl())
+ .contact(new Contact(swaggerProperties.getContact().getName(), swaggerProperties.getContact().getUrl(), swaggerProperties.getContact().getEmail()))
+ .version(swaggerProperties.getVersion())
+ .build();
+ }
+ }
+
diff --git a/ruoyi-common/ruoyi-common-swagger/src/main/java/com/ruoyi/common/swagger/config/SwaggerProperties.java b/ruoyi-common/ruoyi-common-swagger/src/main/java/com/ruoyi/common/swagger/config/SwaggerProperties.java
new file mode 100644
index 0000000..d8d5c84
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-swagger/src/main/java/com/ruoyi/common/swagger/config/SwaggerProperties.java
@@ -0,0 +1,343 @@
+package com.ruoyi.common.swagger.config;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+@ConfigurationProperties("swagger")
+public class SwaggerProperties
+{
+ /**
+ * 是否开启swagger
+ */
+ private Boolean enabled;
+
+ /**
+ * swagger会解析的包路径
+ **/
+ private String basePackage = "";
+
+ /**
+ * swagger会解析的url规则
+ **/
+ private List<String> basePath = new ArrayList<>();
+
+ /**
+ * 在basePath基础上需要排除的url规则
+ **/
+ private List<String> excludePath = new ArrayList<>();
+
+ /**
+ * 标题
+ **/
+ private String title = "";
+
+ /**
+ * 描述
+ **/
+ private String description = "";
+
+ /**
+ * 版本
+ **/
+ private String version = "";
+
+ /**
+ * 许可证
+ **/
+ private String license = "";
+
+ /**
+ * 许可证URL
+ **/
+ private String licenseUrl = "";
+
+ /**
+ * 服务条款URL
+ **/
+ private String termsOfServiceUrl = "";
+
+ /**
+ * host信息
+ **/
+ private String host = "";
+
+ /**
+ * 联系人信息
+ */
+ private Contact contact = new Contact();
+
+ /**
+ * 全局统一鉴权配置
+ **/
+ private Authorization authorization = new Authorization();
+
+ public Boolean getEnabled()
+ {
+ return enabled;
+ }
+
+ public void setEnabled(Boolean enabled)
+ {
+ this.enabled = enabled;
+ }
+
+ public String getBasePackage()
+ {
+ return basePackage;
+ }
+
+ public void setBasePackage(String basePackage)
+ {
+ this.basePackage = basePackage;
+ }
+
+ public List<String> getBasePath()
+ {
+ return basePath;
+ }
+
+ public void setBasePath(List<String> basePath)
+ {
+ this.basePath = basePath;
+ }
+
+ public List<String> getExcludePath()
+ {
+ return excludePath;
+ }
+
+ public void setExcludePath(List<String> excludePath)
+ {
+ this.excludePath = excludePath;
+ }
+
+ public String getTitle()
+ {
+ return title;
+ }
+
+ public void setTitle(String title)
+ {
+ this.title = title;
+ }
+
+ public String getDescription()
+ {
+ return description;
+ }
+
+ public void setDescription(String description)
+ {
+ this.description = description;
+ }
+
+ public String getVersion()
+ {
+ return version;
+ }
+
+ public void setVersion(String version)
+ {
+ this.version = version;
+ }
+
+ public String getLicense()
+ {
+ return license;
+ }
+
+ public void setLicense(String license)
+ {
+ this.license = license;
+ }
+
+ public String getLicenseUrl()
+ {
+ return licenseUrl;
+ }
+
+ public void setLicenseUrl(String licenseUrl)
+ {
+ this.licenseUrl = licenseUrl;
+ }
+
+ public String getTermsOfServiceUrl()
+ {
+ return termsOfServiceUrl;
+ }
+
+ public void setTermsOfServiceUrl(String termsOfServiceUrl)
+ {
+ this.termsOfServiceUrl = termsOfServiceUrl;
+ }
+
+ public String getHost()
+ {
+ return host;
+ }
+
+ public void setHost(String host)
+ {
+ this.host = host;
+ }
+
+ public Contact getContact()
+ {
+ return contact;
+ }
+
+ public void setContact(Contact contact)
+ {
+ this.contact = contact;
+ }
+
+ public Authorization getAuthorization()
+ {
+ return authorization;
+ }
+
+ public void setAuthorization(Authorization authorization)
+ {
+ this.authorization = authorization;
+ }
+
+ public static class Contact
+ {
+ /**
+ * 联系人
+ **/
+ private String name = "";
+ /**
+ * 联系人url
+ **/
+ private String url = "";
+ /**
+ * 联系人email
+ **/
+ private String email = "";
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public void setName(String name)
+ {
+ this.name = name;
+ }
+
+ public String getUrl()
+ {
+ return url;
+ }
+
+ public void setUrl(String url)
+ {
+ this.url = url;
+ }
+
+ public String getEmail()
+ {
+ return email;
+ }
+
+ public void setEmail(String email)
+ {
+ this.email = email;
+ }
+ }
+
+ public static class Authorization
+ {
+ /**
+ * 鉴权策略ID,需要和SecurityReferences ID保持一致
+ */
+ private String name = "";
+
+ /**
+ * 需要开启鉴权URL的正则
+ */
+ private String authRegex = "^.*$";
+
+ /**
+ * 鉴权作用域列表
+ */
+ private List<AuthorizationScope> authorizationScopeList = new ArrayList<>();
+
+ private List<String> tokenUrlList = new ArrayList<>();
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public void setName(String name)
+ {
+ this.name = name;
+ }
+
+ public String getAuthRegex()
+ {
+ return authRegex;
+ }
+
+ public void setAuthRegex(String authRegex)
+ {
+ this.authRegex = authRegex;
+ }
+
+ public List<AuthorizationScope> getAuthorizationScopeList()
+ {
+ return authorizationScopeList;
+ }
+
+ public void setAuthorizationScopeList(List<AuthorizationScope> authorizationScopeList)
+ {
+ this.authorizationScopeList = authorizationScopeList;
+ }
+
+ public List<String> getTokenUrlList()
+ {
+ return tokenUrlList;
+ }
+
+ public void setTokenUrlList(List<String> tokenUrlList)
+ {
+ this.tokenUrlList = tokenUrlList;
+ }
+ }
+
+ public static class AuthorizationScope
+ {
+ /**
+ * 作用域名称
+ */
+ private String scope = "";
+
+ /**
+ * 作用域描述
+ */
+ private String description = "";
+
+ public String getScope()
+ {
+ return scope;
+ }
+
+ public void setScope(String scope)
+ {
+ this.scope = scope;
+ }
+
+ public String getDescription()
+ {
+ return description;
+ }
+
+ public void setDescription(String description)
+ {
+ this.description = description;
+ }
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-common/ruoyi-common-swagger/src/main/resources/META-INF/spring.factories b/ruoyi-common/ruoyi-common-swagger/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000..0cb7c0a
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-swagger/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+ com.ruoyi.common.swagger.config.SwaggerAutoConfiguration
diff --git a/ruoyi-gateway/pom.xml b/ruoyi-gateway/pom.xml
new file mode 100644
index 0000000..cf167a9
--- /dev/null
+++ b/ruoyi-gateway/pom.xml
@@ -0,0 +1,85 @@
+<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">
+ <parent>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi</artifactId>
+ <version>1.0.0</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>ruoyi-gateway</artifactId>
+
+ <description>
+ ruoyi-gateway网关模块
+ </description>
+
+ <dependencies>
+
+ <!-- SpringCloud Gateway-->
+ <dependency>
+ <groupId>org.springframework.cloud</groupId>
+ <artifactId>spring-cloud-starter-gateway</artifactId>
+ </dependency>
+
+ <!-- SpringCloud Ailibaba Nacos -->
+ <dependency>
+ <groupId>com.alibaba.cloud</groupId>
+ <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
+ </dependency>
+
+ <!-- SpringCloud Ailibaba Nacos Config -->
+ <dependency>
+ <groupId>com.alibaba.cloud</groupId>
+ <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
+ </dependency>
+
+ <!-- SpringBoot Actuator -->
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-actuator</artifactId>
+ </dependency>
+
+ <!--验证码 -->
+ <dependency>
+ <groupId>com.github.penggle</groupId>
+ <artifactId>kaptcha</artifactId>
+ </dependency>
+
+ <!-- RuoYi Common Redis-->
+ <dependency>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-common-redis</artifactId>
+ </dependency>
+
+ <!-- Swagger -->
+ <dependency>
+ <groupId>io.springfox</groupId>
+ <artifactId>springfox-swagger-ui</artifactId>
+ <version>${swagger.fox.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>io.springfox</groupId>
+ <artifactId>springfox-swagger2</artifactId>
+ <version>${swagger.fox.version}</version>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>repackage</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/RuoYiGatewayApplication.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/RuoYiGatewayApplication.java
new file mode 100644
index 0000000..d1f6ec1
--- /dev/null
+++ b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/RuoYiGatewayApplication.java
@@ -0,0 +1,31 @@
+package com.ruoyi.gateway;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+
+/**
+ * 网关启动程序
+ *
+ * @author ruoyi
+ */
+@EnableDiscoveryClient
+@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class })
+public class RuoYiGatewayApplication
+{
+ public static void main(String[] args)
+ {
+ SpringApplication.run(RuoYiGatewayApplication.class, args);
+ System.out.println("(♥◠‿◠)ノ゙ 若依网关启动成功 ლ(´ڡ`ლ)゙ \n" +
+ " .-------. ____ __ \n" +
+ " | _ _ \\ \\ \\ / / \n" +
+ " | ( ' ) | \\ _. / ' \n" +
+ " |(_ o _) / _( )_ .' \n" +
+ " | (_,_).' __ ___(_ o _)' \n" +
+ " | |\\ \\ | || |(_,_)' \n" +
+ " | | \\ `' /| `-' / \n" +
+ " | | \\ / \\ / \n" +
+ " ''-' `'-' `-..-' ");
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/CaptchaConfig.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/CaptchaConfig.java
new file mode 100644
index 0000000..c642ffe
--- /dev/null
+++ b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/CaptchaConfig.java
@@ -0,0 +1,57 @@
+package com.ruoyi.gateway.config;
+
+import java.util.Properties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import com.google.code.kaptcha.impl.DefaultKaptcha;
+import com.google.code.kaptcha.util.Config;
+
+/**
+ * 验证码配置
+ *
+ * @author ruoyi
+ */
+@Configuration
+public class CaptchaConfig
+{
+ @Bean(name = "captchaProducerMath")
+ public DefaultKaptcha getKaptchaBeanMath()
+ {
+ DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
+ Properties properties = new Properties();
+ // 是否有边框 默认为true 我们可以自己设置yes,no
+ properties.setProperty("kaptcha.border", "yes");
+ // 边框颜色 默认为Color.BLACK
+ properties.setProperty("kaptcha.border.color", "105,179,90");
+ // 验证码文本字符颜色 默认为Color.BLACK
+ properties.setProperty("kaptcha.textproducer.font.color", "blue");
+ // 验证码图片宽度 默认为200
+ properties.setProperty("kaptcha.image.width", "160");
+ // 验证码图片高度 默认为50
+ properties.setProperty("kaptcha.image.height", "60");
+ // 验证码文本字符大小 默认为40
+ properties.setProperty("kaptcha.textproducer.font.size", "35");
+ // KAPTCHA_SESSION_KEY
+ properties.setProperty("kaptcha.session.key", "kaptchaCodeMath");
+ // 验证码文本生成器
+ properties.setProperty("kaptcha.textproducer.impl", "com.ruoyi.gateway.config.KaptchaTextCreator");
+ // 验证码文本字符间距 默认为2
+ properties.setProperty("kaptcha.textproducer.char.space", "3");
+ // 验证码文本字符长度 默认为5
+ properties.setProperty("kaptcha.textproducer.char.length", "6");
+ // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1,
+ // fontSize)
+ properties.setProperty("kaptcha.textproducer.font.names", "Arial,Courier");
+ // 验证码噪点颜色 默认为Color.BLACK
+ properties.setProperty("kaptcha.noise.color", "white");
+ // 干扰实现类
+ properties.setProperty("kaptcha.noise.impl", "com.google.code.kaptcha.impl.NoNoise");
+ // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple
+ // 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy
+ // 阴影com.google.code.kaptcha.impl.ShadowGimpy
+ properties.setProperty("kaptcha.obscurificator.impl", "com.google.code.kaptcha.impl.ShadowGimpy");
+ Config config = new Config(properties);
+ defaultKaptcha.setConfig(config);
+ return defaultKaptcha;
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/KaptchaTextCreator.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/KaptchaTextCreator.java
new file mode 100644
index 0000000..9ec3173
--- /dev/null
+++ b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/KaptchaTextCreator.java
@@ -0,0 +1,75 @@
+package com.ruoyi.gateway.config;
+
+import java.util.Random;
+import com.google.code.kaptcha.text.impl.DefaultTextCreator;
+
+/**
+ * 验证码文本生成器
+ *
+ * @author ruoyi
+ */
+public class KaptchaTextCreator extends DefaultTextCreator
+{
+ private static final String[] CNUMBERS = "0,1,2,3,4,5,6,7,8,9,10".split(",");
+
+ @Override
+ public String getText()
+ {
+ Integer result = 0;
+ Random random = new Random();
+ int x = random.nextInt(10);
+ int y = random.nextInt(10);
+ StringBuilder suChinese = new StringBuilder();
+ int randomoperands = (int) Math.round(Math.random() * 2);
+ if (randomoperands == 0)
+ {
+ result = x * y;
+ suChinese.append(CNUMBERS[x]);
+ suChinese.append("*");
+ suChinese.append(CNUMBERS[y]);
+ }
+ else if (randomoperands == 1)
+ {
+ if (!(x == 0) && y % x == 0)
+ {
+ result = y / x;
+ suChinese.append(CNUMBERS[y]);
+ suChinese.append("/");
+ suChinese.append(CNUMBERS[x]);
+ }
+ else
+ {
+ result = x + y;
+ suChinese.append(CNUMBERS[x]);
+ suChinese.append("+");
+ suChinese.append(CNUMBERS[y]);
+ }
+ }
+ else if (randomoperands == 2)
+ {
+ if (x >= y)
+ {
+ result = x - y;
+ suChinese.append(CNUMBERS[x]);
+ suChinese.append("-");
+ suChinese.append(CNUMBERS[y]);
+ }
+ else
+ {
+ result = y - x;
+ suChinese.append(CNUMBERS[y]);
+ suChinese.append("-");
+ suChinese.append(CNUMBERS[x]);
+ }
+ }
+ else
+ {
+ result = x + y;
+ suChinese.append(CNUMBERS[x]);
+ suChinese.append("+");
+ suChinese.append(CNUMBERS[y]);
+ }
+ suChinese.append("=?@" + result);
+ return suChinese.toString();
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/RouterFunctionConfiguration.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/RouterFunctionConfiguration.java
new file mode 100644
index 0000000..b923b00
--- /dev/null
+++ b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/RouterFunctionConfiguration.java
@@ -0,0 +1,38 @@
+package com.ruoyi.gateway.config;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.MediaType;
+import org.springframework.web.reactive.function.server.RequestPredicates;
+import org.springframework.web.reactive.function.server.RouterFunction;
+import org.springframework.web.reactive.function.server.RouterFunctions;
+import com.ruoyi.gateway.handler.HystrixFallbackHandler;
+import com.ruoyi.gateway.handler.ValidateCodeHandler;
+
+/**
+ * 路由配置信息
+ *
+ * @author ruoyi
+ */
+@Configuration
+public class RouterFunctionConfiguration
+{
+ @Autowired
+ private HystrixFallbackHandler hystrixFallbackHandler;
+
+ @Autowired
+ private ValidateCodeHandler imageCodeHandler;
+
+ @SuppressWarnings("rawtypes")
+ @Bean
+ public RouterFunction routerFunction()
+ {
+ return RouterFunctions
+ .route(RequestPredicates.path("/fallback").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)),
+ hystrixFallbackHandler)
+ .andRoute(RequestPredicates.GET("/code").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)),
+ imageCodeHandler);
+ }
+
+}
diff --git a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/SwaggerProvider.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/SwaggerProvider.java
new file mode 100644
index 0000000..c0bdd7a
--- /dev/null
+++ b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/SwaggerProvider.java
@@ -0,0 +1,61 @@
+package com.ruoyi.gateway.config;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cloud.gateway.config.GatewayProperties;
+import org.springframework.cloud.gateway.route.RouteLocator;
+import org.springframework.cloud.gateway.support.NameUtils;
+import org.springframework.stereotype.Component;
+import springfox.documentation.swagger.web.SwaggerResource;
+import springfox.documentation.swagger.web.SwaggerResourcesProvider;
+
+@Component
+public class SwaggerProvider implements SwaggerResourcesProvider
+{
+ /**
+ * Swagger2默认的url后缀
+ */
+ public static final String SWAGGER2URL = "/v2/api-docs";
+ /**
+ * 网关路由
+ */
+ @Autowired
+ private RouteLocator routeLocator;
+
+ @Autowired
+ private GatewayProperties gatewayProperties;
+
+ /**
+ * 聚合其他服务接口
+ *
+ * @return
+ */
+ @Override
+ public List<SwaggerResource> get()
+ {
+ List<SwaggerResource> resourceList = new ArrayList<>();
+ List<String> routes = new ArrayList<>();
+ // 获取网关中配置的route
+ routeLocator.getRoutes().subscribe(route -> routes.add(route.getId()));
+ gatewayProperties.getRoutes().stream()
+ .filter(routeDefinition -> routes
+ .contains(routeDefinition.getId()))
+ .forEach(routeDefinition -> routeDefinition.getPredicates().stream()
+ .filter(predicateDefinition -> "Path".equalsIgnoreCase(predicateDefinition.getName()))
+ .filter(predicateDefinition -> !"ruoyi-auth".equalsIgnoreCase(routeDefinition.getId()))
+ .forEach(predicateDefinition -> resourceList
+ .add(swaggerResource(routeDefinition.getId(), predicateDefinition.getArgs()
+ .get(NameUtils.GENERATED_NAME_PREFIX + "0").replace("/**", SWAGGER2URL)))));
+ return resourceList;
+ }
+
+ private SwaggerResource swaggerResource(String name, String location)
+ {
+ SwaggerResource swaggerResource = new SwaggerResource();
+ swaggerResource.setName(name);
+ swaggerResource.setLocation(location);
+ swaggerResource.setSwaggerVersion("2.0");
+ return swaggerResource;
+ }
+}
diff --git a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/properties/IgnoreClientProperties.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/properties/IgnoreClientProperties.java
new file mode 100644
index 0000000..7d6c985
--- /dev/null
+++ b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/properties/IgnoreClientProperties.java
@@ -0,0 +1,33 @@
+package com.ruoyi.gateway.config.properties;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 放行终端配置
+ *
+ * @author ruoyi
+ */
+@Configuration
+@RefreshScope
+@ConfigurationProperties(prefix = "ignore")
+public class IgnoreClientProperties
+{
+ /**
+ * 放行终端配置,网关不校验此处的终端
+ */
+ private List<String> clients = new ArrayList<>();
+
+ public List<String> getClients()
+ {
+ return clients;
+ }
+
+ public void setClients(List<String> clients)
+ {
+ this.clients = clients;
+ }
+}
diff --git a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/ValidateCodeFilter.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/ValidateCodeFilter.java
new file mode 100644
index 0000000..8d6a8a5
--- /dev/null
+++ b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/ValidateCodeFilter.java
@@ -0,0 +1,53 @@
+package com.ruoyi.gateway.filter;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cloud.gateway.filter.GatewayFilter;
+import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.stereotype.Component;
+import com.alibaba.fastjson.JSON;
+import com.ruoyi.common.core.utils.StringUtils;
+import com.ruoyi.common.core.web.domain.AjaxResult;
+import com.ruoyi.gateway.service.ValidateCodeService;
+import reactor.core.publisher.Mono;
+
+/**
+ * 验证码过滤器
+ *
+ * @author ruoyi
+ */
+@Component
+public class ValidateCodeFilter extends AbstractGatewayFilterFactory<Object>
+{
+ private final static String AUTH_URL = "/oauth/token";
+
+ @Autowired
+ private ValidateCodeService validateCodeService;
+
+ @Override
+ public GatewayFilter apply(Object config)
+ {
+ return (exchange, chain) -> {
+ ServerHttpRequest request = exchange.getRequest();
+
+ // 非登录请求,不处理
+ if (!StringUtils.containsIgnoreCase(request.getURI().getPath(), AUTH_URL))
+ {
+ return chain.filter(exchange);
+ }
+ try
+ {
+ validateCodeService.checkCapcha(request.getQueryParams().getFirst("code"),
+ request.getQueryParams().getFirst("uuid"));
+ }
+ catch (Exception e)
+ {
+ ServerHttpResponse response = exchange.getResponse();
+ return exchange.getResponse().writeWith(
+ Mono.just(response.bufferFactory().wrap(JSON.toJSONBytes(AjaxResult.error(e.getMessage())))));
+ }
+ return chain.filter(exchange);
+ };
+ }
+}
diff --git a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/HystrixFallbackHandler.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/HystrixFallbackHandler.java
new file mode 100644
index 0000000..d49f424
--- /dev/null
+++ b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/HystrixFallbackHandler.java
@@ -0,0 +1,36 @@
+package com.ruoyi.gateway.handler;
+
+import com.alibaba.fastjson.JSON;
+import com.ruoyi.common.core.domain.R;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Component;
+import org.springframework.web.reactive.function.BodyInserters;
+import org.springframework.web.reactive.function.server.HandlerFunction;
+import org.springframework.web.reactive.function.server.ServerRequest;
+import org.springframework.web.reactive.function.server.ServerResponse;
+import reactor.core.publisher.Mono;
+import java.util.Optional;
+import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR;
+
+/**
+ * 熔断降级处理
+ *
+ * @author ruoyi
+ */
+@Component
+public class HystrixFallbackHandler implements HandlerFunction<ServerResponse>
+{
+ private static final Logger log = LoggerFactory.getLogger(HystrixFallbackHandler.class);
+
+ @Override
+ public Mono<ServerResponse> handle(ServerRequest serverRequest)
+ {
+ Optional<Object> originalUris = serverRequest.attribute(GATEWAY_ORIGINAL_REQUEST_URL_ATTR);
+ originalUris.ifPresent(originalUri -> log.error("网关执行请求:{}失败,hystrix服务降级处理", originalUri));
+ return ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR.value()).contentType(MediaType.APPLICATION_JSON)
+ .body(BodyInserters.fromValue(JSON.toJSONString(R.failed("服务已被降级熔断"))));
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/SwaggerHandler.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/SwaggerHandler.java
new file mode 100644
index 0000000..af1e7ad
--- /dev/null
+++ b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/SwaggerHandler.java
@@ -0,0 +1,56 @@
+package com.ruoyi.gateway.handler;
+
+import java.util.Optional;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import reactor.core.publisher.Mono;
+import springfox.documentation.swagger.web.SecurityConfiguration;
+import springfox.documentation.swagger.web.SecurityConfigurationBuilder;
+import springfox.documentation.swagger.web.SwaggerResourcesProvider;
+import springfox.documentation.swagger.web.UiConfiguration;
+import springfox.documentation.swagger.web.UiConfigurationBuilder;
+
+@RestController
+@RequestMapping("/swagger-resources")
+public class SwaggerHandler
+{
+ @Autowired(required = false)
+ private SecurityConfiguration securityConfiguration;
+
+ @Autowired(required = false)
+ private UiConfiguration uiConfiguration;
+
+ private final SwaggerResourcesProvider swaggerResources;
+
+ @Autowired
+ public SwaggerHandler(SwaggerResourcesProvider swaggerResources)
+ {
+ this.swaggerResources = swaggerResources;
+ }
+
+ @GetMapping("/configuration/security")
+ public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration()
+ {
+ return Mono.just(new ResponseEntity<>(
+ Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()),
+ HttpStatus.OK));
+ }
+
+ @GetMapping("/configuration/ui")
+ public Mono<ResponseEntity<UiConfiguration>> uiConfiguration()
+ {
+ return Mono.just(new ResponseEntity<>(
+ Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
+ }
+
+ @SuppressWarnings("rawtypes")
+ @GetMapping("")
+ public Mono<ResponseEntity> swaggerResources()
+ {
+ return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
+ }
+}
diff --git a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/ValidateCodeHandler.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/ValidateCodeHandler.java
new file mode 100644
index 0000000..af5601b
--- /dev/null
+++ b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/ValidateCodeHandler.java
@@ -0,0 +1,41 @@
+package com.ruoyi.gateway.handler;
+
+import java.io.IOException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.stereotype.Component;
+import org.springframework.web.reactive.function.BodyInserters;
+import org.springframework.web.reactive.function.server.HandlerFunction;
+import org.springframework.web.reactive.function.server.ServerRequest;
+import org.springframework.web.reactive.function.server.ServerResponse;
+import com.ruoyi.common.core.exception.CaptchaException;
+import com.ruoyi.common.core.web.domain.AjaxResult;
+import com.ruoyi.gateway.service.ValidateCodeService;
+import reactor.core.publisher.Mono;
+
+/**
+ * 验证码获取
+ *
+ * @author ruoyi
+ */
+@Component
+public class ValidateCodeHandler implements HandlerFunction<ServerResponse>
+{
+ @Autowired
+ private ValidateCodeService validateCodeService;
+
+ @Override
+ public Mono<ServerResponse> handle(ServerRequest serverRequest)
+ {
+ AjaxResult ajax;
+ try
+ {
+ ajax = validateCodeService.createCapcha();
+ }
+ catch (CaptchaException | IOException e)
+ {
+ return Mono.error(e);
+ }
+ return ServerResponse.status(HttpStatus.OK).body(BodyInserters.fromValue(ajax));
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/service/ValidateCodeService.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/service/ValidateCodeService.java
new file mode 100644
index 0000000..3ae9436
--- /dev/null
+++ b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/service/ValidateCodeService.java
@@ -0,0 +1,23 @@
+package com.ruoyi.gateway.service;
+
+import java.io.IOException;
+import com.ruoyi.common.core.exception.CaptchaException;
+import com.ruoyi.common.core.web.domain.AjaxResult;
+
+/**
+ * 验证码处理
+ *
+ * @author ruoyi
+ */
+public interface ValidateCodeService
+{
+ /**
+ * 生成验证码
+ */
+ public AjaxResult createCapcha() throws IOException, CaptchaException;
+
+ /**
+ * 校验验证码
+ */
+ public void checkCapcha(String key, String value) throws CaptchaException;
+}
diff --git a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/service/impl/ValidateCodeServiceImpl.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/service/impl/ValidateCodeServiceImpl.java
new file mode 100644
index 0000000..738897c
--- /dev/null
+++ b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/service/impl/ValidateCodeServiceImpl.java
@@ -0,0 +1,93 @@
+package com.ruoyi.gateway.service.impl;
+
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+import javax.imageio.ImageIO;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.util.FastByteArrayOutputStream;
+
+import com.google.code.kaptcha.Producer;
+import com.ruoyi.common.core.constant.Constants;
+import com.ruoyi.common.core.exception.CaptchaException;
+import com.ruoyi.common.core.utils.IdUtils;
+import com.ruoyi.common.core.utils.StringUtils;
+import com.ruoyi.common.core.utils.sign.Base64;
+import com.ruoyi.common.core.web.domain.AjaxResult;
+import com.ruoyi.common.redis.service.RedisService;
+import com.ruoyi.gateway.service.ValidateCodeService;
+
+/**
+ * 验证码实现处理
+ *
+ * @author ruoyi
+ */
+@Service
+public class ValidateCodeServiceImpl implements ValidateCodeService
+{
+ @Autowired
+ private Producer producer;
+
+ @Autowired
+ private RedisService redisService;
+
+ /**
+ * 生成验证码
+ */
+ @Override
+ public AjaxResult createCapcha() throws IOException, CaptchaException
+ {
+ // 生成验证码
+ String capText = producer.createText();
+ String capStr = capText.substring(0, capText.lastIndexOf("@"));
+ String verifyCode = capText.substring(capText.lastIndexOf("@") + 1);
+ BufferedImage image = producer.createImage(capStr);
+ // 保存验证码信息
+ String uuid = IdUtils.simpleUUID();
+ String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;
+
+ redisService.setCacheObject(verifyKey, verifyCode, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
+ // 转换流信息写出
+ FastByteArrayOutputStream os = new FastByteArrayOutputStream();
+ try
+ {
+ ImageIO.write(image, "jpg", os);
+ }
+ catch (IOException e)
+ {
+ return AjaxResult.error(e.getMessage());
+ }
+
+ AjaxResult ajax = AjaxResult.success();
+ ajax.put("uuid", uuid);
+ ajax.put("img", Base64.encode(os.toByteArray()));
+ return ajax;
+ }
+
+ /**
+ * 校验验证码
+ */
+ @Override
+ public void checkCapcha(String code, String uuid) throws CaptchaException
+ {
+ if (StringUtils.isEmpty(code))
+ {
+ throw new CaptchaException("验证码不能为空");
+ }
+ if (StringUtils.isEmpty(uuid))
+ {
+ throw new CaptchaException("验证码已失效");
+ }
+ String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;
+ String captcha = redisService.getCacheObject(verifyKey);
+ redisService.deleteObject(verifyKey);
+
+ if (!code.equalsIgnoreCase(captcha))
+ {
+ throw new CaptchaException("验证码错误");
+ }
+ }
+}
diff --git a/ruoyi-gateway/src/main/resources/banner.txt b/ruoyi-gateway/src/main/resources/banner.txt
new file mode 100644
index 0000000..fc8e118
--- /dev/null
+++ b/ruoyi-gateway/src/main/resources/banner.txt
@@ -0,0 +1,10 @@
+Spring Boot Version: ${spring-boot.version}
+Spring Application Name: ${spring.application.name}
+ _ _
+ (_) | |
+ _ __ _ _ ___ _ _ _ ______ __ _ __ _ | |_ ___ __ __ __ _ _ _
+| '__|| | | | / _ \ | | | || ||______| / _` | / _` || __| / _ \\ \ /\ / / / _` || | | |
+| | | |_| || (_) || |_| || | | (_| || (_| || |_ | __/ \ V V / | (_| || |_| |
+|_| \__,_| \___/ \__, ||_| \__, | \__,_| \__| \___| \_/\_/ \__,_| \__, |
+ __/ | __/ | __/ |
+ |___/ |___/ |___/
\ No newline at end of file
diff --git a/ruoyi-gateway/src/main/resources/bootstrap.yml b/ruoyi-gateway/src/main/resources/bootstrap.yml
new file mode 100644
index 0000000..a8c5370
--- /dev/null
+++ b/ruoyi-gateway/src/main/resources/bootstrap.yml
@@ -0,0 +1,24 @@
+# Tomcat
+server:
+ port: 8080
+
+# Spring
+spring:
+ application:
+ # 应用名称
+ name: ruoyi-gateway
+ profiles:
+ # 环境配置
+ active: dev
+ cloud:
+ nacos:
+ discovery:
+ # 服务注册地址
+ server-addr: 127.0.0.1:8848
+ config:
+ # 配置中心地址
+ server-addr: 127.0.0.1:8848
+ # 配置文件格式
+ file-extension: yml
+ # 共享配置
+ shared-dataids: application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
diff --git a/ruoyi-modules/pom.xml b/ruoyi-modules/pom.xml
new file mode 100644
index 0000000..92d74bb
--- /dev/null
+++ b/ruoyi-modules/pom.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi</artifactId>
+ <version>1.0.0</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <modules>
+ <module>ruoyi-system</module>
+ <module>ruoyi-gen</module>
+ <module>ruoyi-job</module>
+ </modules>
+
+ <artifactId>ruoyi-modules</artifactId>
+ <packaging>pom</packaging>
+
+ <description>
+ ruoyi-modules业务模块
+ </description>
+
+</project>
diff --git a/ruoyi-modules/ruoyi-gen/pom.xml b/ruoyi-modules/ruoyi-gen/pom.xml
new file mode 100644
index 0000000..a148ab6
--- /dev/null
+++ b/ruoyi-modules/ruoyi-gen/pom.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns="http://maven.apache.org/POM/4.0.0"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-modules</artifactId>
+ <version>1.0.0</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>ruoyi-modules-gen</artifactId>
+
+ <description>
+ ruoyi-modules-gen代码生成
+ </description>
+
+ <dependencies>
+
+ <!-- SpringCloud Ailibaba Nacos -->
+ <dependency>
+ <groupId>com.alibaba.cloud</groupId>
+ <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
+ </dependency>
+
+ <!-- SpringCloud Ailibaba Nacos Config -->
+ <dependency>
+ <groupId>com.alibaba.cloud</groupId>
+ <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
+ </dependency>
+
+ <!-- SpringCloud Netflix Hystrix -->
+ <dependency>
+ <groupId>org.springframework.cloud</groupId>
+ <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
+ </dependency>
+
+ <!-- SpringBoot Web -->
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-web</artifactId>
+ </dependency>
+
+ <!-- Swagger -->
+ <dependency>
+ <groupId>io.springfox</groupId>
+ <artifactId>springfox-swagger-ui</artifactId>
+ <version>${swagger.fox.version}</version>
+ </dependency>
+
+ <!-- Apache Velocity -->
+ <dependency>
+ <groupId>org.apache.velocity</groupId>
+ <artifactId>velocity</artifactId>
+ </dependency>
+
+ <!-- Mysql Connector -->
+ <dependency>
+ <groupId>mysql</groupId>
+ <artifactId>mysql-connector-java</artifactId>
+ </dependency>
+
+ <!-- Ruoyi Common Security-->
+ <dependency>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-common-security</artifactId>
+ </dependency>
+
+ <!-- Ruoyi Common Log -->
+ <dependency>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-common-log</artifactId>
+ </dependency>
+
+ <!-- Ruoyi Common Swagger -->
+ <dependency>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-common-swagger</artifactId>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>repackage</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/RuoYiGenApplication.java b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/RuoYiGenApplication.java
new file mode 100644
index 0000000..955cbeb
--- /dev/null
+++ b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/RuoYiGenApplication.java
@@ -0,0 +1,34 @@
+package com.ruoyi.gen;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.cloud.client.SpringCloudApplication;
+import com.ruoyi.common.security.annotation.EnableCustomConfig;
+import com.ruoyi.common.security.annotation.EnableRyFeignClients;
+import com.ruoyi.common.swagger.annotation.EnableCustomSwagger2;
+
+/**
+ * 代码生成
+ *
+ * @author ruoyi
+ */
+@EnableCustomConfig
+@EnableCustomSwagger2
+@EnableRyFeignClients
+@SpringCloudApplication
+public class RuoYiGenApplication
+{
+ public static void main(String[] args)
+ {
+ SpringApplication.run(RuoYiGenApplication.class, args);
+ System.out.println("(♥◠‿◠)ノ゙ 代码生成模块启动成功 ლ(´ڡ`ლ)゙ \n" +
+ " .-------. ____ __ \n" +
+ " | _ _ \\ \\ \\ / / \n" +
+ " | ( ' ) | \\ _. / ' \n" +
+ " |(_ o _) / _( )_ .' \n" +
+ " | (_,_).' __ ___(_ o _)' \n" +
+ " | |\\ \\ | || |(_,_)' \n" +
+ " | | \\ `' /| `-' / \n" +
+ " | | \\ / \\ / \n" +
+ " ''-' `'-' `-..-' ");
+ }
+}
diff --git a/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/config/GenConfig.java b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/config/GenConfig.java
new file mode 100644
index 0000000..7bfdfe2
--- /dev/null
+++ b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/config/GenConfig.java
@@ -0,0 +1,66 @@
+package com.ruoyi.gen.config;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * 代码生成相关配置
+ *
+ * @author ruoyi
+ */
+@Component
+@ConfigurationProperties(prefix = "gen")
+public class GenConfig
+{
+ /** 作者 */
+ public static String author;
+
+ /** 生成包路径 */
+ public static String packageName;
+
+ /** 自动去除表前缀,默认是false */
+ public static boolean autoRemovePre;
+
+ /** 表前缀(类名不会包含表前缀) */
+ public static String tablePrefix;
+
+ public static String getAuthor()
+ {
+ return author;
+ }
+
+ public void setAuthor(String author)
+ {
+ GenConfig.author = author;
+ }
+
+ public static String getPackageName()
+ {
+ return packageName;
+ }
+
+ public void setPackageName(String packageName)
+ {
+ GenConfig.packageName = packageName;
+ }
+
+ public static boolean getAutoRemovePre()
+ {
+ return autoRemovePre;
+ }
+
+ public void setAutoRemovePre(boolean autoRemovePre)
+ {
+ GenConfig.autoRemovePre = autoRemovePre;
+ }
+
+ public static String getTablePrefix()
+ {
+ return tablePrefix;
+ }
+
+ public void setTablePrefix(String tablePrefix)
+ {
+ GenConfig.tablePrefix = tablePrefix;
+ }
+}
diff --git a/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/controller/GenController.java b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/controller/GenController.java
new file mode 100644
index 0000000..f4492f5
--- /dev/null
+++ b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/controller/GenController.java
@@ -0,0 +1,186 @@
+package com.ruoyi.gen.controller;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.commons.io.IOUtils;
+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.RestController;
+import com.ruoyi.common.core.text.Convert;
+import com.ruoyi.common.core.web.controller.BaseController;
+import com.ruoyi.common.core.web.domain.AjaxResult;
+import com.ruoyi.common.core.web.page.TableDataInfo;
+import com.ruoyi.common.log.annotation.Log;
+import com.ruoyi.common.log.enums.BusinessType;
+import com.ruoyi.gen.domain.GenTable;
+import com.ruoyi.gen.domain.GenTableColumn;
+import com.ruoyi.gen.service.IGenTableColumnService;
+import com.ruoyi.gen.service.IGenTableService;
+
+/**
+ * 代码生成 操作处理
+ *
+ * @author ruoyi
+ */
+@RequestMapping("/gen")
+@RestController
+public class GenController extends BaseController
+{
+ @Autowired
+ private IGenTableService genTableService;
+
+ @Autowired
+ private IGenTableColumnService genTableColumnService;
+
+ /**
+ * 查询代码生成列表
+ */
+ @PreAuthorize("@ss.hasPermi('tool:gen:list')")
+ @GetMapping("/list")
+ public TableDataInfo genList(GenTable genTable)
+ {
+ startPage();
+ List<GenTable> list = genTableService.selectGenTableList(genTable);
+ return getDataTable(list);
+ }
+
+ /**
+ * 修改代码生成业务
+ */
+ @PreAuthorize("@ss.hasPermi('tool:gen:query')")
+ @GetMapping(value = "/{talbleId}")
+ public AjaxResult getInfo(@PathVariable Long talbleId)
+ {
+ GenTable table = genTableService.selectGenTableById(talbleId);
+ List<GenTableColumn> list = genTableColumnService.selectGenTableColumnListByTableId(talbleId);
+ Map<String, Object> map = new HashMap<String, Object>();
+ map.put("info", table);
+ map.put("rows", list);
+ return AjaxResult.success(map);
+ }
+
+ /**
+ * 查询数据库列表
+ */
+ @PreAuthorize("@ss.hasPermi('tool:gen:list')")
+ @GetMapping("/db/list")
+ public TableDataInfo dataList(GenTable genTable)
+ {
+ startPage();
+ List<GenTable> list = genTableService.selectDbTableList(genTable);
+ return getDataTable(list);
+ }
+
+ /**
+ * 查询数据表字段列表
+ */
+ @PreAuthorize("@ss.hasPermi('tool:gen:list')")
+ @GetMapping(value = "/column/{talbleId}")
+ public TableDataInfo columnList(Long tableId)
+ {
+ TableDataInfo dataInfo = new TableDataInfo();
+ List<GenTableColumn> list = genTableColumnService.selectGenTableColumnListByTableId(tableId);
+ dataInfo.setRows(list);
+ dataInfo.setTotal(list.size());
+ return dataInfo;
+ }
+
+ /**
+ * 导入表结构(保存)
+ */
+ @PreAuthorize("@ss.hasPermi('tool:gen:list')")
+ @Log(title = "代码生成", businessType = BusinessType.IMPORT)
+ @PostMapping("/importTable")
+ public AjaxResult importTableSave(String tables)
+ {
+ String[] tableNames = Convert.toStrArray(tables);
+ // 查询表信息
+ List<GenTable> tableList = genTableService.selectDbTableListByNames(tableNames);
+ genTableService.importGenTable(tableList);
+ return AjaxResult.success();
+ }
+
+ /**
+ * 修改保存代码生成业务
+ */
+ @PreAuthorize("@ss.hasPermi('tool:gen:edit')")
+ @Log(title = "代码生成", businessType = BusinessType.UPDATE)
+ @PutMapping
+ public AjaxResult editSave(@Validated @RequestBody GenTable genTable)
+ {
+ genTableService.validateEdit(genTable);
+ genTableService.updateGenTable(genTable);
+ return AjaxResult.success();
+ }
+
+ /**
+ * 删除代码生成
+ */
+ @PreAuthorize("@ss.hasPermi('tool:gen:remove')")
+ @Log(title = "代码生成", businessType = BusinessType.DELETE)
+ @DeleteMapping("/{tableIds}")
+ public AjaxResult remove(@PathVariable Long[] tableIds)
+ {
+ genTableService.deleteGenTableByIds(tableIds);
+ return AjaxResult.success();
+ }
+
+ /**
+ * 预览代码
+ */
+ @PreAuthorize("@ss.hasPermi('tool:gen:preview')")
+ @GetMapping("/preview/{tableId}")
+ public AjaxResult preview(@PathVariable("tableId") Long tableId) throws IOException
+ {
+ Map<String, String> dataMap = genTableService.previewCode(tableId);
+ return AjaxResult.success(dataMap);
+ }
+
+ /**
+ * 生成代码
+ */
+ @PreAuthorize("@ss.hasPermi('tool:gen:code')")
+ @Log(title = "代码生成", businessType = BusinessType.GENCODE)
+ @GetMapping("/genCode/{tableName}")
+ public void genCode(HttpServletResponse response, @PathVariable("tableName") String tableName) throws IOException
+ {
+ byte[] data = genTableService.generatorCode(tableName);
+ genCode(response, data);
+ }
+
+ /**
+ * 批量生成代码
+ */
+ @PreAuthorize("@ss.hasPermi('tool:gen:code')")
+ @Log(title = "代码生成", businessType = BusinessType.GENCODE)
+ @GetMapping("/batchGenCode")
+ public void batchGenCode(HttpServletResponse response, String tables) throws IOException
+ {
+ String[] tableNames = Convert.toStrArray(tables);
+ byte[] data = genTableService.generatorCode(tableNames);
+ genCode(response, data);
+ }
+
+ /**
+ * 生成zip文件
+ */
+ private void genCode(HttpServletResponse response, byte[] data) throws IOException
+ {
+ response.reset();
+ response.setHeader("Content-Disposition", "attachment; filename=\"ruoyi.zip\"");
+ response.addHeader("Content-Length", "" + data.length);
+ response.setContentType("application/octet-stream; charset=UTF-8");
+ IOUtils.write(data, response.getOutputStream());
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/domain/GenTable.java b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/domain/GenTable.java
new file mode 100644
index 0000000..86fc86a
--- /dev/null
+++ b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/domain/GenTable.java
@@ -0,0 +1,271 @@
+package com.ruoyi.gen.domain;
+
+import java.util.List;
+import javax.validation.Valid;
+import javax.validation.constraints.NotBlank;
+import org.apache.commons.lang3.ArrayUtils;
+import com.ruoyi.common.core.constant.GenConstants;
+import com.ruoyi.common.core.utils.StringUtils;
+import com.ruoyi.common.core.web.domain.BaseEntity;
+
+/**
+ * 业务表 gen_table
+ *
+ * @author ruoyi
+ */
+public class GenTable extends BaseEntity
+{
+ private static final long serialVersionUID = 1L;
+
+ /** 编号 */
+ private Long tableId;
+
+ /** 表名称 */
+ @NotBlank(message = "表名称不能为空")
+ private String tableName;
+
+ /** 表描述 */
+ @NotBlank(message = "表描述不能为空")
+ private String tableComment;
+
+ /** 实体类名称(首字母大写) */
+ @NotBlank(message = "实体类名称不能为空")
+ private String className;
+
+ /** 使用的模板(crud单表操作 tree树表操作) */
+ private String tplCategory;
+
+ /** 生成包路径 */
+ @NotBlank(message = "生成包路径不能为空")
+ private String packageName;
+
+ /** 生成模块名 */
+ @NotBlank(message = "生成模块名不能为空")
+ private String moduleName;
+
+ /** 生成业务名 */
+ @NotBlank(message = "生成业务名不能为空")
+ private String businessName;
+
+ /** 生成功能名 */
+ @NotBlank(message = "生成功能名不能为空")
+ private String functionName;
+
+ /** 生成作者 */
+ @NotBlank(message = "作者不能为空")
+ private String functionAuthor;
+
+ /** 主键信息 */
+ private GenTableColumn pkColumn;
+
+ /** 表列信息 */
+ @Valid
+ private List<GenTableColumn> columns;
+
+ /** 其它生成选项 */
+ private String options;
+
+ /** 树编码字段 */
+ private String treeCode;
+
+ /** 树父编码字段 */
+ private String treeParentCode;
+
+ /** 树名称字段 */
+ private String treeName;
+
+ public Long getTableId()
+ {
+ return tableId;
+ }
+
+ public void setTableId(Long tableId)
+ {
+ this.tableId = tableId;
+ }
+
+ public String getTableName()
+ {
+ return tableName;
+ }
+
+ public void setTableName(String tableName)
+ {
+ this.tableName = tableName;
+ }
+
+ public String getTableComment()
+ {
+ return tableComment;
+ }
+
+ public void setTableComment(String tableComment)
+ {
+ this.tableComment = tableComment;
+ }
+
+ public String getClassName()
+ {
+ return className;
+ }
+
+ public void setClassName(String className)
+ {
+ this.className = className;
+ }
+
+ public String getTplCategory()
+ {
+ return tplCategory;
+ }
+
+ public void setTplCategory(String tplCategory)
+ {
+ this.tplCategory = tplCategory;
+ }
+
+ public String getPackageName()
+ {
+ return packageName;
+ }
+
+ public void setPackageName(String packageName)
+ {
+ this.packageName = packageName;
+ }
+
+ public String getModuleName()
+ {
+ return moduleName;
+ }
+
+ public void setModuleName(String moduleName)
+ {
+ this.moduleName = moduleName;
+ }
+
+ public String getBusinessName()
+ {
+ return businessName;
+ }
+
+ public void setBusinessName(String businessName)
+ {
+ this.businessName = businessName;
+ }
+
+ public String getFunctionName()
+ {
+ return functionName;
+ }
+
+ public void setFunctionName(String functionName)
+ {
+ this.functionName = functionName;
+ }
+
+ public String getFunctionAuthor()
+ {
+ return functionAuthor;
+ }
+
+ public void setFunctionAuthor(String functionAuthor)
+ {
+ this.functionAuthor = functionAuthor;
+ }
+
+ public GenTableColumn getPkColumn()
+ {
+ return pkColumn;
+ }
+
+ public void setPkColumn(GenTableColumn pkColumn)
+ {
+ this.pkColumn = pkColumn;
+ }
+
+ public List<GenTableColumn> getColumns()
+ {
+ return columns;
+ }
+
+ public void setColumns(List<GenTableColumn> columns)
+ {
+ this.columns = columns;
+ }
+
+ public String getOptions()
+ {
+ return options;
+ }
+
+ public void setOptions(String options)
+ {
+ this.options = options;
+ }
+
+ public String getTreeCode()
+ {
+ return treeCode;
+ }
+
+ public void setTreeCode(String treeCode)
+ {
+ this.treeCode = treeCode;
+ }
+
+ public String getTreeParentCode()
+ {
+ return treeParentCode;
+ }
+
+ public void setTreeParentCode(String treeParentCode)
+ {
+ this.treeParentCode = treeParentCode;
+ }
+
+ public String getTreeName()
+ {
+ return treeName;
+ }
+
+ public void setTreeName(String treeName)
+ {
+ this.treeName = treeName;
+ }
+
+ public boolean isTree()
+ {
+ return isTree(this.tplCategory);
+ }
+
+ public static boolean isTree(String tplCategory)
+ {
+ return tplCategory != null && StringUtils.equals(GenConstants.TPL_TREE, tplCategory);
+ }
+
+ public boolean isCrud()
+ {
+ return isCrud(this.tplCategory);
+ }
+
+ public static boolean isCrud(String tplCategory)
+ {
+ return tplCategory != null && StringUtils.equals(GenConstants.TPL_CRUD, tplCategory);
+ }
+
+ public boolean isSuperColumn(String javaField)
+ {
+ return isSuperColumn(this.tplCategory, javaField);
+ }
+
+ public static boolean isSuperColumn(String tplCategory, String javaField)
+ {
+ if (isTree(tplCategory))
+ {
+ return StringUtils.equalsAnyIgnoreCase(javaField,
+ ArrayUtils.addAll(GenConstants.TREE_ENTITY, GenConstants.BASE_ENTITY));
+ }
+ return StringUtils.equalsAnyIgnoreCase(javaField, GenConstants.BASE_ENTITY);
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/domain/GenTableColumn.java b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/domain/GenTableColumn.java
new file mode 100644
index 0000000..d69e219
--- /dev/null
+++ b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/domain/GenTableColumn.java
@@ -0,0 +1,369 @@
+package com.ruoyi.gen.domain;
+
+import javax.validation.constraints.NotBlank;
+
+import com.ruoyi.common.core.utils.StringUtils;
+import com.ruoyi.common.core.web.domain.BaseEntity;
+
+/**
+ * 代码生成业务字段表 gen_table_column
+ *
+ * @author ruoyi
+ */
+public class GenTableColumn extends BaseEntity
+{
+ private static final long serialVersionUID = 1L;
+
+ /** 编号 */
+ private Long columnId;
+
+ /** 归属表编号 */
+ private Long tableId;
+
+ /** 列名称 */
+ private String columnName;
+
+ /** 列描述 */
+ private String columnComment;
+
+ /** 列类型 */
+ private String columnType;
+
+ /** JAVA类型 */
+ private String javaType;
+
+ /** JAVA字段名 */
+ @NotBlank(message = "Java属性不能为空")
+ private String javaField;
+
+ /** 是否主键(1是) */
+ private String isPk;
+
+ /** 是否自增(1是) */
+ private String isIncrement;
+
+ /** 是否必填(1是) */
+ private String isRequired;
+
+ /** 是否为插入字段(1是) */
+ private String isInsert;
+
+ /** 是否编辑字段(1是) */
+ private String isEdit;
+
+ /** 是否列表字段(1是) */
+ private String isList;
+
+ /** 是否查询字段(1是) */
+ private String isQuery;
+
+ /** 查询方式(EQ等于、NE不等于、GT大于、LT小于、LIKE模糊、BETWEEN范围) */
+ private String queryType;
+
+ /** 显示类型(input文本框、textarea文本域、select下拉框、checkbox复选框、radio单选框、datetime日期控件) */
+ private String htmlType;
+
+ /** 字典类型 */
+ private String dictType;
+
+ /** 排序 */
+ private Integer sort;
+
+ public void setColumnId(Long columnId)
+ {
+ this.columnId = columnId;
+ }
+
+ public Long getColumnId()
+ {
+ return columnId;
+ }
+
+ public void setTableId(Long tableId)
+ {
+ this.tableId = tableId;
+ }
+
+ public Long getTableId()
+ {
+ return tableId;
+ }
+
+ public void setColumnName(String columnName)
+ {
+ this.columnName = columnName;
+ }
+
+ public String getColumnName()
+ {
+ return columnName;
+ }
+
+ public void setColumnComment(String columnComment)
+ {
+ this.columnComment = columnComment;
+ }
+
+ public String getColumnComment()
+ {
+ return columnComment;
+ }
+
+ public void setColumnType(String columnType)
+ {
+ this.columnType = columnType;
+ }
+
+ public String getColumnType()
+ {
+ return columnType;
+ }
+
+ public void setJavaType(String javaType)
+ {
+ this.javaType = javaType;
+ }
+
+ public String getJavaType()
+ {
+ return javaType;
+ }
+
+ public void setJavaField(String javaField)
+ {
+ this.javaField = javaField;
+ }
+
+ public String getJavaField()
+ {
+ return javaField;
+ }
+
+ public void setIsPk(String isPk)
+ {
+ this.isPk = isPk;
+ }
+
+ public String getIsPk()
+ {
+ return isPk;
+ }
+
+ public boolean isPk()
+ {
+ return isPk(this.isPk);
+ }
+
+ public boolean isPk(String isPk)
+ {
+ return isPk != null && StringUtils.equals("1", isPk);
+ }
+
+ public String getIsIncrement()
+ {
+ return isIncrement;
+ }
+
+ public void setIsIncrement(String isIncrement)
+ {
+ this.isIncrement = isIncrement;
+ }
+
+ public boolean isIncrement()
+ {
+ return isIncrement(this.isIncrement);
+ }
+
+ public boolean isIncrement(String isIncrement)
+ {
+ return isIncrement != null && StringUtils.equals("1", isIncrement);
+ }
+
+ public void setIsRequired(String isRequired)
+ {
+ this.isRequired = isRequired;
+ }
+
+ public String getIsRequired()
+ {
+ return isRequired;
+ }
+
+ public boolean isRequired()
+ {
+ return isRequired(this.isRequired);
+ }
+
+ public boolean isRequired(String isRequired)
+ {
+ return isRequired != null && StringUtils.equals("1", isRequired);
+ }
+
+ public void setIsInsert(String isInsert)
+ {
+ this.isInsert = isInsert;
+ }
+
+ public String getIsInsert()
+ {
+ return isInsert;
+ }
+
+ public boolean isInsert()
+ {
+ return isInsert(this.isInsert);
+ }
+
+ public boolean isInsert(String isInsert)
+ {
+ return isInsert != null && StringUtils.equals("1", isInsert);
+ }
+
+ public void setIsEdit(String isEdit)
+ {
+ this.isEdit = isEdit;
+ }
+
+ public String getIsEdit()
+ {
+ return isEdit;
+ }
+
+ public boolean isEdit()
+ {
+ return isInsert(this.isEdit);
+ }
+
+ public boolean isEdit(String isEdit)
+ {
+ return isEdit != null && StringUtils.equals("1", isEdit);
+ }
+
+ public void setIsList(String isList)
+ {
+ this.isList = isList;
+ }
+
+ public String getIsList()
+ {
+ return isList;
+ }
+
+ public boolean isList()
+ {
+ return isList(this.isList);
+ }
+
+ public boolean isList(String isList)
+ {
+ return isList != null && StringUtils.equals("1", isList);
+ }
+
+ public void setIsQuery(String isQuery)
+ {
+ this.isQuery = isQuery;
+ }
+
+ public String getIsQuery()
+ {
+ return isQuery;
+ }
+
+ public boolean isQuery()
+ {
+ return isQuery(this.isQuery);
+ }
+
+ public boolean isQuery(String isQuery)
+ {
+ return isQuery != null && StringUtils.equals("1", isQuery);
+ }
+
+ public void setQueryType(String queryType)
+ {
+ this.queryType = queryType;
+ }
+
+ public String getQueryType()
+ {
+ return queryType;
+ }
+
+ public String getHtmlType()
+ {
+ return htmlType;
+ }
+
+ public void setHtmlType(String htmlType)
+ {
+ this.htmlType = htmlType;
+ }
+
+ public void setDictType(String dictType)
+ {
+ this.dictType = dictType;
+ }
+
+ public String getDictType()
+ {
+ return dictType;
+ }
+
+ public void setSort(Integer sort)
+ {
+ this.sort = sort;
+ }
+
+ public Integer getSort()
+ {
+ return sort;
+ }
+
+ public boolean isSuperColumn()
+ {
+ return isSuperColumn(this.javaField);
+ }
+
+ public static boolean isSuperColumn(String javaField)
+ {
+ return StringUtils.equalsAnyIgnoreCase(javaField,
+ // BaseEntity
+ "createBy", "createTime", "updateBy", "updateTime", "remark",
+ // TreeEntity
+ "parentName", "parentId", "orderNum", "ancestors");
+ }
+
+ public boolean isUsableColumn()
+ {
+ return isUsableColumn(javaField);
+ }
+
+ public static boolean isUsableColumn(String javaField)
+ {
+ // isSuperColumn()中的名单用于避免生成多余Domain属性,若某些属性在生成页面时需要用到不能忽略,则放在此处白名单
+ return StringUtils.equalsAnyIgnoreCase(javaField, "parentId", "orderNum");
+ }
+
+ public String readConverterExp()
+ {
+ String remarks = StringUtils.substringBetween(this.columnComment, "(", ")");
+ StringBuffer sb = new StringBuffer();
+ if (StringUtils.isNotEmpty(remarks))
+ {
+ for (String value : remarks.split(" "))
+ {
+ if (StringUtils.isNotEmpty(value))
+ {
+ Object startStr = value.subSequence(0, 1);
+ String endStr = value.substring(1);
+ sb.append("").append(startStr).append("=").append(endStr).append(",");
+ }
+ }
+ return sb.deleteCharAt(sb.length() - 1).toString();
+ }
+ else
+ {
+ return this.columnComment;
+ }
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/mapper/GenTableColumnMapper.java b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/mapper/GenTableColumnMapper.java
new file mode 100644
index 0000000..20fb80e
--- /dev/null
+++ b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/mapper/GenTableColumnMapper.java
@@ -0,0 +1,52 @@
+package com.ruoyi.gen.mapper;
+
+import java.util.List;
+import com.ruoyi.gen.domain.GenTableColumn;
+
+/**
+ * 业务字段 数据层
+ *
+ * @author ruoyi
+ */
+public interface GenTableColumnMapper
+{
+ /**
+ * 根据表名称查询列信息
+ *
+ * @param tableName 表名称
+ * @return 列信息
+ */
+ public List<GenTableColumn> selectDbTableColumnsByName(String tableName);
+
+ /**
+ * 查询业务字段列表
+ *
+ * @param tableId 业务字段编号
+ * @return 业务字段集合
+ */
+ public List<GenTableColumn> selectGenTableColumnListByTableId(Long tableId);
+
+ /**
+ * 新增业务字段
+ *
+ * @param genTableColumn 业务字段信息
+ * @return 结果
+ */
+ public int insertGenTableColumn(GenTableColumn genTableColumn);
+
+ /**
+ * 修改业务字段
+ *
+ * @param genTableColumn 业务字段信息
+ * @return 结果
+ */
+ public int updateGenTableColumn(GenTableColumn genTableColumn);
+
+ /**
+ * 批量删除业务字段
+ *
+ * @param ids 需要删除的数据ID
+ * @return 结果
+ */
+ public int deleteGenTableColumnByIds(Long[] ids);
+}
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/mapper/GenTableMapper.java b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/mapper/GenTableMapper.java
new file mode 100644
index 0000000..b5ef298
--- /dev/null
+++ b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/mapper/GenTableMapper.java
@@ -0,0 +1,76 @@
+package com.ruoyi.gen.mapper;
+
+import java.util.List;
+import com.ruoyi.gen.domain.GenTable;
+
+/**
+ * 业务 数据层
+ *
+ * @author ruoyi
+ */
+public interface GenTableMapper
+{
+ /**
+ * 查询业务列表
+ *
+ * @param genTable 业务信息
+ * @return 业务集合
+ */
+ public List<GenTable> selectGenTableList(GenTable genTable);
+
+ /**
+ * 查询据库列表
+ *
+ * @param genTable 业务信息
+ * @return 数据库表集合
+ */
+ public List<GenTable> selectDbTableList(GenTable genTable);
+
+ /**
+ * 查询据库列表
+ *
+ * @param tableNames 表名称组
+ * @return 数据库表集合
+ */
+ public List<GenTable> selectDbTableListByNames(String[] tableNames);
+
+ /**
+ * 查询表ID业务信息
+ *
+ * @param id 业务ID
+ * @return 业务信息
+ */
+ public GenTable selectGenTableById(Long id);
+
+ /**
+ * 查询表名称业务信息
+ *
+ * @param tableName 表名称
+ * @return 业务信息
+ */
+ public GenTable selectGenTableByName(String tableName);
+
+ /**
+ * 新增业务
+ *
+ * @param genTable 业务信息
+ * @return 结果
+ */
+ public int insertGenTable(GenTable genTable);
+
+ /**
+ * 修改业务
+ *
+ * @param genTable 业务信息
+ * @return 结果
+ */
+ public int updateGenTable(GenTable genTable);
+
+ /**
+ * 批量删除业务
+ *
+ * @param ids 需要删除的数据ID
+ * @return 结果
+ */
+ public int deleteGenTableByIds(Long[] ids);
+}
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/service/GenTableColumnServiceImpl.java b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/service/GenTableColumnServiceImpl.java
new file mode 100644
index 0000000..268a4f0
--- /dev/null
+++ b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/service/GenTableColumnServiceImpl.java
@@ -0,0 +1,68 @@
+package com.ruoyi.gen.service;
+
+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.gen.domain.GenTableColumn;
+import com.ruoyi.gen.mapper.GenTableColumnMapper;
+
+/**
+ * 业务字段 服务层实现
+ *
+ * @author ruoyi
+ */
+@Service
+public class GenTableColumnServiceImpl implements IGenTableColumnService
+{
+ @Autowired
+ private GenTableColumnMapper genTableColumnMapper;
+
+ /**
+ * 查询业务字段列表
+ *
+ * @param genTableColumn 业务字段编号
+ * @return 业务字段集合
+ */
+ @Override
+ public List<GenTableColumn> selectGenTableColumnListByTableId(Long tableId)
+ {
+ return genTableColumnMapper.selectGenTableColumnListByTableId(tableId);
+ }
+
+ /**
+ * 新增业务字段
+ *
+ * @param genTableColumn 业务字段信息
+ * @return 结果
+ */
+ @Override
+ public int insertGenTableColumn(GenTableColumn genTableColumn)
+ {
+ return genTableColumnMapper.insertGenTableColumn(genTableColumn);
+ }
+
+ /**
+ * 修改业务字段
+ *
+ * @param genTableColumn 业务字段信息
+ * @return 结果
+ */
+ @Override
+ public int updateGenTableColumn(GenTableColumn genTableColumn)
+ {
+ return genTableColumnMapper.updateGenTableColumn(genTableColumn);
+ }
+
+ /**
+ * 删除业务字段对象
+ *
+ * @param ids 需要删除的数据ID
+ * @return 结果
+ */
+ @Override
+ public int deleteGenTableColumnByIds(String ids)
+ {
+ return genTableColumnMapper.deleteGenTableColumnByIds(Convert.toLongArray(ids));
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/service/GenTableServiceImpl.java b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/service/GenTableServiceImpl.java
new file mode 100644
index 0000000..e327e09
--- /dev/null
+++ b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/service/GenTableServiceImpl.java
@@ -0,0 +1,340 @@
+package com.ruoyi.gen.service;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+import org.apache.commons.io.IOUtils;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.ruoyi.common.core.constant.Constants;
+import com.ruoyi.common.core.constant.GenConstants;
+import com.ruoyi.common.core.exception.CustomException;
+import com.ruoyi.common.core.utils.StringUtils;
+import com.ruoyi.gen.domain.GenTable;
+import com.ruoyi.gen.domain.GenTableColumn;
+import com.ruoyi.gen.mapper.GenTableColumnMapper;
+import com.ruoyi.gen.mapper.GenTableMapper;
+import com.ruoyi.gen.util.GenUtils;
+import com.ruoyi.gen.util.VelocityInitializer;
+import com.ruoyi.gen.util.VelocityUtils;
+
+/**
+ * 业务 服务层实现
+ *
+ * @author ruoyi
+ */
+@Service
+public class GenTableServiceImpl implements IGenTableService
+{
+ private static final Logger log = LoggerFactory.getLogger(GenTableServiceImpl.class);
+
+ @Autowired
+ private GenTableMapper genTableMapper;
+
+ @Autowired
+ private GenTableColumnMapper genTableColumnMapper;
+
+ /**
+ * 查询业务信息
+ *
+ * @param id 业务ID
+ * @return 业务信息
+ */
+ @Override
+ public GenTable selectGenTableById(Long id)
+ {
+ GenTable genTable = genTableMapper.selectGenTableById(id);
+ setTableFromOptions(genTable);
+ return genTable;
+ }
+
+ /**
+ * 查询业务列表
+ *
+ * @param genTable 业务信息
+ * @return 业务集合
+ */
+ @Override
+ public List<GenTable> selectGenTableList(GenTable genTable)
+ {
+ return genTableMapper.selectGenTableList(genTable);
+ }
+
+ /**
+ * 查询据库列表
+ *
+ * @param genTable 业务信息
+ * @return 数据库表集合
+ */
+ public List<GenTable> selectDbTableList(GenTable genTable)
+ {
+ return genTableMapper.selectDbTableList(genTable);
+ }
+
+ /**
+ * 查询据库列表
+ *
+ * @param tableNames 表名称组
+ * @return 数据库表集合
+ */
+ public List<GenTable> selectDbTableListByNames(String[] tableNames)
+ {
+ return genTableMapper.selectDbTableListByNames(tableNames);
+ }
+
+ /**
+ * 修改业务
+ *
+ * @param genTable 业务信息
+ * @return 结果
+ */
+ @Override
+ @Transactional
+ public void updateGenTable(GenTable genTable)
+ {
+ String options = JSON.toJSONString(genTable.getParams());
+ genTable.setOptions(options);
+ int row = genTableMapper.updateGenTable(genTable);
+ if (row > 0)
+ {
+ for (GenTableColumn cenTableColumn : genTable.getColumns())
+ {
+ genTableColumnMapper.updateGenTableColumn(cenTableColumn);
+ }
+ }
+ }
+
+ /**
+ * 删除业务对象
+ *
+ * @param ids 需要删除的数据ID
+ * @return 结果
+ */
+ @Override
+ @Transactional
+ public void deleteGenTableByIds(Long[] tableIds)
+ {
+ genTableMapper.deleteGenTableByIds(tableIds);
+ genTableColumnMapper.deleteGenTableColumnByIds(tableIds);
+ }
+
+ /**
+ * 导入表结构
+ *
+ * @param tableList 导入表列表
+ */
+ @Override
+ @Transactional
+ public void importGenTable(List<GenTable> tableList)
+ {
+ String operName = "";
+ for (GenTable table : tableList)
+ {
+ try
+ {
+ String tableName = table.getTableName();
+ GenUtils.initTable(table, operName);
+ int row = genTableMapper.insertGenTable(table);
+ if (row > 0)
+ {
+ // 保存列信息
+ List<GenTableColumn> genTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName);
+ for (GenTableColumn column : genTableColumns)
+ {
+ GenUtils.initColumnField(column, table);
+ genTableColumnMapper.insertGenTableColumn(column);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ log.error("表名 " + table.getTableName() + " 导入失败:", e);
+ }
+ }
+ }
+
+ /**
+ * 预览代码
+ *
+ * @param tableId 表编号
+ * @return 预览数据列表
+ */
+ public Map<String, String> previewCode(Long tableId)
+ {
+ Map<String, String> dataMap = new LinkedHashMap<>();
+ // 查询表信息
+ GenTable table = genTableMapper.selectGenTableById(tableId);
+ // 查询列信息
+ List<GenTableColumn> columns = table.getColumns();
+ setPkColumn(table, columns);
+ VelocityInitializer.initVelocity();
+
+ VelocityContext context = VelocityUtils.prepareContext(table);
+
+ // 获取模板列表
+ List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory());
+ for (String template : templates)
+ {
+ // 渲染模板
+ StringWriter sw = new StringWriter();
+ Template tpl = Velocity.getTemplate(template, Constants.UTF8);
+ tpl.merge(context, sw);
+ dataMap.put(template, sw.toString());
+ }
+ return dataMap;
+ }
+
+ /**
+ * 生成代码
+ *
+ * @param tableName 表名称
+ * @return 数据
+ */
+ @Override
+ public byte[] generatorCode(String tableName)
+ {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ ZipOutputStream zip = new ZipOutputStream(outputStream);
+ generatorCode(tableName, zip);
+ IOUtils.closeQuietly(zip);
+ return outputStream.toByteArray();
+ }
+
+ /**
+ * 批量生成代码
+ *
+ * @param tableNames 表数组
+ * @return 数据
+ */
+ @Override
+ public byte[] generatorCode(String[] tableNames)
+ {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ ZipOutputStream zip = new ZipOutputStream(outputStream);
+ for (String tableName : tableNames)
+ {
+ generatorCode(tableName, zip);
+ }
+ IOUtils.closeQuietly(zip);
+ return outputStream.toByteArray();
+ }
+
+ /**
+ * 查询表信息并生成代码
+ */
+ private void generatorCode(String tableName, ZipOutputStream zip)
+ {
+ // 查询表信息
+ GenTable table = genTableMapper.selectGenTableByName(tableName);
+ // 查询列信息
+ List<GenTableColumn> columns = table.getColumns();
+ setPkColumn(table, columns);
+
+ VelocityInitializer.initVelocity();
+
+ VelocityContext context = VelocityUtils.prepareContext(table);
+
+ // 获取模板列表
+ List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory());
+ for (String template : templates)
+ {
+ // 渲染模板
+ StringWriter sw = new StringWriter();
+ Template tpl = Velocity.getTemplate(template, Constants.UTF8);
+ tpl.merge(context, sw);
+ try
+ {
+ // 添加到zip
+ zip.putNextEntry(new ZipEntry(VelocityUtils.getFileName(template, table)));
+ IOUtils.write(sw.toString(), zip, Constants.UTF8);
+ IOUtils.closeQuietly(sw);
+ zip.flush();
+ zip.closeEntry();
+ }
+ catch (IOException e)
+ {
+ log.error("渲染模板失败,表名:" + table.getTableName(), e);
+ }
+ }
+ }
+
+ /**
+ * 修改保存参数校验
+ *
+ * @param genTable 业务信息
+ */
+ public void validateEdit(GenTable genTable)
+ {
+ if (GenConstants.TPL_TREE.equals(genTable.getTplCategory()))
+ {
+ String options = JSON.toJSONString(genTable.getParams());
+ JSONObject paramsObj = JSONObject.parseObject(options);
+ if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_CODE)))
+ {
+ throw new CustomException("树编码字段不能为空");
+ }
+ else if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_PARENT_CODE)))
+ {
+ throw new CustomException("树父编码字段不能为空");
+ }
+ else if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_NAME)))
+ {
+ throw new CustomException("树名称字段不能为空");
+ }
+ }
+ }
+
+ /**
+ * 设置主键列信息
+ *
+ * @param genTable 业务表信息
+ * @param columns 业务字段列表
+ */
+ public void setPkColumn(GenTable table, List<GenTableColumn> columns)
+ {
+ for (GenTableColumn column : columns)
+ {
+ if (column.isPk())
+ {
+ table.setPkColumn(column);
+ break;
+ }
+ }
+ if (StringUtils.isNull(table.getPkColumn()))
+ {
+ table.setPkColumn(columns.get(0));
+ }
+ }
+
+ /**
+ * 设置代码生成其他选项值
+ *
+ * @param genTable 设置后的生成对象
+ */
+ public void setTableFromOptions(GenTable genTable)
+ {
+ JSONObject paramsObj = JSONObject.parseObject(genTable.getOptions());
+ if (StringUtils.isNotNull(paramsObj))
+ {
+ String treeCode = paramsObj.getString(GenConstants.TREE_CODE);
+ String treeParentCode = paramsObj.getString(GenConstants.TREE_PARENT_CODE);
+ String treeName = paramsObj.getString(GenConstants.TREE_NAME);
+ genTable.setTreeCode(treeCode);
+ genTable.setTreeParentCode(treeParentCode);
+ genTable.setTreeName(treeName);
+ }
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/service/IGenTableColumnService.java b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/service/IGenTableColumnService.java
new file mode 100644
index 0000000..67e432c
--- /dev/null
+++ b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/service/IGenTableColumnService.java
@@ -0,0 +1,44 @@
+package com.ruoyi.gen.service;
+
+import java.util.List;
+import com.ruoyi.gen.domain.GenTableColumn;
+
+/**
+ * 业务字段 服务层
+ *
+ * @author ruoyi
+ */
+public interface IGenTableColumnService
+{
+ /**
+ * 查询业务字段列表
+ *
+ * @param genTableColumn 业务字段编号
+ * @return 业务字段集合
+ */
+ public List<GenTableColumn> selectGenTableColumnListByTableId(Long tableId);
+
+ /**
+ * 新增业务字段
+ *
+ * @param genTableColumn 业务字段信息
+ * @return 结果
+ */
+ public int insertGenTableColumn(GenTableColumn genTableColumn);
+
+ /**
+ * 修改业务字段
+ *
+ * @param genTableColumn 业务字段信息
+ * @return 结果
+ */
+ public int updateGenTableColumn(GenTableColumn genTableColumn);
+
+ /**
+ * 删除业务字段信息
+ *
+ * @param ids 需要删除的数据ID
+ * @return 结果
+ */
+ public int deleteGenTableColumnByIds(String ids);
+}
diff --git a/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/service/IGenTableService.java b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/service/IGenTableService.java
new file mode 100644
index 0000000..050646c
--- /dev/null
+++ b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/service/IGenTableService.java
@@ -0,0 +1,99 @@
+package com.ruoyi.gen.service;
+
+import java.util.List;
+import java.util.Map;
+import com.ruoyi.gen.domain.GenTable;
+
+/**
+ * 业务 服务层
+ *
+ * @author ruoyi
+ */
+public interface IGenTableService
+{
+ /**
+ * 查询业务列表
+ *
+ * @param genTable 业务信息
+ * @return 业务集合
+ */
+ public List<GenTable> selectGenTableList(GenTable genTable);
+
+ /**
+ * 查询据库列表
+ *
+ * @param genTable 业务信息
+ * @return 数据库表集合
+ */
+ public List<GenTable> selectDbTableList(GenTable genTable);
+
+ /**
+ * 查询据库列表
+ *
+ * @param tableNames 表名称组
+ * @return 数据库表集合
+ */
+ public List<GenTable> selectDbTableListByNames(String[] tableNames);
+
+ /**
+ * 查询业务信息
+ *
+ * @param id 业务ID
+ * @return 业务信息
+ */
+ public GenTable selectGenTableById(Long id);
+
+ /**
+ * 修改业务
+ *
+ * @param genTable 业务信息
+ * @return 结果
+ */
+ public void updateGenTable(GenTable genTable);
+
+ /**
+ * 删除业务信息
+ *
+ * @param tableIds 需要删除的表数据ID
+ * @return 结果
+ */
+ public void deleteGenTableByIds(Long[] tableIds);
+
+ /**
+ * 导入表结构
+ *
+ * @param tableList 导入表列表
+ */
+ public void importGenTable(List<GenTable> tableList);
+
+ /**
+ * 预览代码
+ *
+ * @param tableId 表编号
+ * @return 预览数据列表
+ */
+ public Map<String, String> previewCode(Long tableId);
+
+ /**
+ * 生成代码
+ *
+ * @param tableName 表名称
+ * @return 数据
+ */
+ public byte[] generatorCode(String tableName);
+
+ /**
+ * 批量生成代码
+ *
+ * @param tableNames 表数组
+ * @return 数据
+ */
+ public byte[] generatorCode(String[] tableNames);
+
+ /**
+ * 修改保存参数校验
+ *
+ * @param genTable 业务信息
+ */
+ public void validateEdit(GenTable genTable);
+}
diff --git a/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/util/GenUtils.java b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/util/GenUtils.java
new file mode 100644
index 0000000..cfc4753
--- /dev/null
+++ b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/util/GenUtils.java
@@ -0,0 +1,242 @@
+package com.ruoyi.gen.util;
+
+import java.util.Arrays;
+import org.apache.commons.lang3.RegExUtils;
+import com.ruoyi.common.core.constant.GenConstants;
+import com.ruoyi.common.core.utils.StringUtils;
+import com.ruoyi.gen.config.GenConfig;
+import com.ruoyi.gen.domain.GenTable;
+import com.ruoyi.gen.domain.GenTableColumn;
+
+/**
+ * 代码生成器 工具类
+ *
+ * @author ruoyi
+ */
+public class GenUtils
+{
+ /**
+ * 初始化表信息
+ */
+ public static void initTable(GenTable genTable, String operName)
+ {
+ genTable.setClassName(convertClassName(genTable.getTableName()));
+ genTable.setPackageName(GenConfig.getPackageName());
+ genTable.setModuleName(getModuleName(GenConfig.getPackageName()));
+ genTable.setBusinessName(getBusinessName(genTable.getTableName()));
+ genTable.setFunctionName(replaceText(genTable.getTableComment()));
+ genTable.setFunctionAuthor(GenConfig.getAuthor());
+ genTable.setCreateBy(operName);
+ }
+
+ /**
+ * 初始化列属性字段
+ */
+ public static void initColumnField(GenTableColumn column, GenTable table)
+ {
+ String dataType = getDbType(column.getColumnType());
+ String columnName = column.getColumnName();
+ column.setTableId(table.getTableId());
+ column.setCreateBy(table.getCreateBy());
+ // 设置java字段名
+ column.setJavaField(StringUtils.toCamelCase(columnName));
+
+ if (arraysContains(GenConstants.COLUMNTYPE_STR, dataType))
+ {
+ column.setJavaType(GenConstants.TYPE_STRING);
+ // 字符串长度超过500设置为文本域
+ Integer columnLength = getColumnLength(column.getColumnType());
+ String htmlType = columnLength >= 500 ? GenConstants.HTML_TEXTAREA : GenConstants.HTML_INPUT;
+ column.setHtmlType(htmlType);
+ }
+ else if (arraysContains(GenConstants.COLUMNTYPE_TIME, dataType))
+ {
+ column.setJavaType(GenConstants.TYPE_DATE);
+ column.setHtmlType(GenConstants.HTML_DATETIME);
+ }
+ else if (arraysContains(GenConstants.COLUMNTYPE_NUMBER, dataType))
+ {
+ column.setHtmlType(GenConstants.HTML_INPUT);
+
+ // 如果是浮点型
+ String[] str = StringUtils.split(StringUtils.substringBetween(column.getColumnType(), "(", ")"), ",");
+ if (str != null && str.length == 2 && Integer.parseInt(str[1]) > 0)
+ {
+ column.setJavaType(GenConstants.TYPE_DOUBLE);
+ }
+ // 如果是整形
+ else if (str != null && str.length == 1 && Integer.parseInt(str[0]) <= 10)
+ {
+ column.setJavaType(GenConstants.TYPE_INTEGER);
+ }
+ // 长整形
+ else
+ {
+ column.setJavaType(GenConstants.TYPE_LONG);
+ }
+ }
+
+ // 插入字段(默认所有字段都需要插入)
+ column.setIsInsert(GenConstants.REQUIRE);
+
+ // 编辑字段
+ if (!arraysContains(GenConstants.COLUMNNAME_NOT_EDIT, columnName) && !column.isPk())
+ {
+ column.setIsEdit(GenConstants.REQUIRE);
+ }
+ // 列表字段
+ if (!arraysContains(GenConstants.COLUMNNAME_NOT_LIST, columnName) && !column.isPk())
+ {
+ column.setIsList(GenConstants.REQUIRE);
+ }
+ // 查询字段
+ if (!arraysContains(GenConstants.COLUMNNAME_NOT_QUERY, columnName) && !column.isPk())
+ {
+ column.setIsQuery(GenConstants.REQUIRE);
+ }
+
+ // 查询字段类型
+ if (StringUtils.endsWithIgnoreCase(columnName, "name"))
+ {
+ column.setQueryType(GenConstants.QUERY_LIKE);
+ }
+ // 状态字段设置单选框
+ if (StringUtils.endsWithIgnoreCase(columnName, "status"))
+ {
+ column.setHtmlType(GenConstants.HTML_RADIO);
+ }
+ // 类型&性别字段设置下拉框
+ else if (StringUtils.endsWithIgnoreCase(columnName, "type")
+ || StringUtils.endsWithIgnoreCase(columnName, "sex"))
+ {
+ column.setHtmlType(GenConstants.HTML_SELECT);
+ }
+ }
+
+ /**
+ * 校验数组是否包含指定值
+ *
+ * @param arr 数组
+ * @param targetValue 值
+ * @return 是否包含
+ */
+ public static boolean arraysContains(String[] arr, String targetValue)
+ {
+ return Arrays.asList(arr).contains(targetValue);
+ }
+
+ /**
+ * 获取模块名
+ *
+ * @param packageName 包名
+ * @return 模块名
+ */
+ public static String getModuleName(String packageName)
+ {
+ int lastIndex = packageName.lastIndexOf(".");
+ int nameLength = packageName.length();
+ String moduleName = StringUtils.substring(packageName, lastIndex + 1, nameLength);
+ return moduleName;
+ }
+
+ /**
+ * 获取业务名
+ *
+ * @param tableName 表名
+ * @return 业务名
+ */
+ public static String getBusinessName(String tableName)
+ {
+ int lastIndex = tableName.lastIndexOf("_");
+ int nameLength = tableName.length();
+ String businessName = StringUtils.substring(tableName, lastIndex + 1, nameLength);
+ return businessName;
+ }
+
+ /**
+ * 表名转换成Java类名
+ *
+ * @param tableName 表名称
+ * @return 类名
+ */
+ public static String convertClassName(String tableName)
+ {
+ boolean autoRemovePre = GenConfig.getAutoRemovePre();
+ String tablePrefix = GenConfig.getTablePrefix();
+ if (autoRemovePre && StringUtils.isNotEmpty(tablePrefix))
+ {
+ String[] searchList = StringUtils.split(tablePrefix, ",");
+ tableName = replaceFirst(tableName, searchList);
+ }
+ return StringUtils.convertToCamelCase(tableName);
+ }
+
+ /**
+ * 批量替换前缀
+ *
+ * @param replacementm 替换值
+ * @param searchList 替换列表
+ * @return
+ */
+ public static String replaceFirst(String replacementm, String[] searchList)
+ {
+ String text = replacementm;
+ for (String searchString : searchList)
+ {
+ if (replacementm.startsWith(searchString))
+ {
+ text = replacementm.replaceFirst(searchString, "");
+ break;
+ }
+ }
+ return text;
+ }
+
+ /**
+ * 关键字替换
+ *
+ * @param name 需要被替换的名字
+ * @return 替换后的名字
+ */
+ public static String replaceText(String text)
+ {
+ return RegExUtils.replaceAll(text, "(?:表|若依)", "");
+ }
+
+ /**
+ * 获取数据库类型字段
+ *
+ * @param columnType 列类型
+ * @return 截取后的列类型
+ */
+ public static String getDbType(String columnType)
+ {
+ if (StringUtils.indexOf(columnType, "(") > 0)
+ {
+ return StringUtils.substringBefore(columnType, "(");
+ }
+ else
+ {
+ return columnType;
+ }
+ }
+
+ /**
+ * 获取字段长度
+ *
+ * @param columnType 列类型
+ * @return 截取后的列类型
+ */
+ public static Integer getColumnLength(String columnType)
+ {
+ if (StringUtils.indexOf(columnType, "(") > 0)
+ {
+ String length = StringUtils.substringBetween(columnType, "(", ")");
+ return Integer.valueOf(length);
+ }
+ else
+ {
+ return 0;
+ }
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/util/VelocityInitializer.java b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/util/VelocityInitializer.java
new file mode 100644
index 0000000..09af096
--- /dev/null
+++ b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/util/VelocityInitializer.java
@@ -0,0 +1,35 @@
+package com.ruoyi.gen.util;
+
+import java.util.Properties;
+import org.apache.velocity.app.Velocity;
+import com.ruoyi.common.core.constant.Constants;
+
+/**
+ * VelocityEngine工厂
+ *
+ * @author ruoyi
+ */
+public class VelocityInitializer
+{
+ /**
+ * 初始化vm方法
+ */
+ public static void initVelocity()
+ {
+ Properties p = new Properties();
+ try
+ {
+ // 加载classpath目录下的vm文件
+ p.setProperty("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
+ // 定义字符集
+ p.setProperty(Velocity.ENCODING_DEFAULT, Constants.UTF8);
+ p.setProperty(Velocity.OUTPUT_ENCODING, Constants.UTF8);
+ // 初始化Velocity引擎,指定配置Properties
+ Velocity.init(p);
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/util/VelocityUtils.java b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/util/VelocityUtils.java
new file mode 100644
index 0000000..09b1145
--- /dev/null
+++ b/ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/util/VelocityUtils.java
@@ -0,0 +1,298 @@
+package com.ruoyi.gen.util;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import org.apache.velocity.VelocityContext;
+import com.alibaba.fastjson.JSONObject;
+import com.ruoyi.common.core.constant.GenConstants;
+import com.ruoyi.common.core.utils.DateUtils;
+import com.ruoyi.common.core.utils.StringUtils;
+import com.ruoyi.gen.domain.GenTable;
+import com.ruoyi.gen.domain.GenTableColumn;
+
+/**
+ * 模板工具类
+ *
+ * @author ruoyi
+ */
+public class VelocityUtils
+{
+ /** 项目空间路径 */
+ private static final String PROJECT_PATH = "main/java";
+
+ /** mybatis空间路径 */
+ private static final String MYBATIS_PATH = "main/resources/mybatis";
+
+ /**
+ * 设置模板变量信息
+ *
+ * @return 模板列表
+ */
+ public static VelocityContext prepareContext(GenTable genTable)
+ {
+ String moduleName = genTable.getModuleName();
+ String businessName = genTable.getBusinessName();
+ String packageName = genTable.getPackageName();
+ String tplCategory = genTable.getTplCategory();
+ String functionName = genTable.getFunctionName();
+
+ VelocityContext velocityContext = new VelocityContext();
+ velocityContext.put("tplCategory", genTable.getTplCategory());
+ velocityContext.put("tableName", genTable.getTableName());
+ velocityContext.put("functionName", StringUtils.isNotEmpty(functionName) ? functionName : "【请填写功能名称】");
+ velocityContext.put("ClassName", genTable.getClassName());
+ velocityContext.put("className", StringUtils.uncapitalize(genTable.getClassName()));
+ velocityContext.put("moduleName", genTable.getModuleName());
+ velocityContext.put("BusinessName", StringUtils.capitalize(genTable.getBusinessName()));
+ velocityContext.put("businessName", genTable.getBusinessName());
+ velocityContext.put("basePackage", getPackagePrefix(packageName));
+ velocityContext.put("packageName", packageName);
+ velocityContext.put("author", genTable.getFunctionAuthor());
+ velocityContext.put("datetime", DateUtils.getDate());
+ velocityContext.put("pkColumn", genTable.getPkColumn());
+ velocityContext.put("importList", getImportList(genTable.getColumns()));
+ velocityContext.put("permissionPrefix", getPermissionPrefix(moduleName, businessName));
+ velocityContext.put("columns", genTable.getColumns());
+ velocityContext.put("table", genTable);
+ if (GenConstants.TPL_TREE.equals(tplCategory))
+ {
+ setTreeVelocityContext(velocityContext, genTable);
+ }
+ return velocityContext;
+ }
+
+ public static void setTreeVelocityContext(VelocityContext context, GenTable genTable)
+ {
+ String options = genTable.getOptions();
+ JSONObject paramsObj = JSONObject.parseObject(options);
+ String treeCode = getTreecode(paramsObj);
+ String treeParentCode = getTreeParentCode(paramsObj);
+ String treeName = getTreeName(paramsObj);
+
+ context.put("treeCode", treeCode);
+ context.put("treeParentCode", treeParentCode);
+ context.put("treeName", treeName);
+ context.put("expandColumn", getExpandColumn(genTable));
+ if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE))
+ {
+ context.put("tree_parent_code", paramsObj.getString(GenConstants.TREE_PARENT_CODE));
+ }
+ if (paramsObj.containsKey(GenConstants.TREE_NAME))
+ {
+ context.put("tree_name", paramsObj.getString(GenConstants.TREE_NAME));
+ }
+ }
+
+ /**
+ * 获取模板信息
+ *
+ * @return 模板列表
+ */
+ public static List<String> getTemplateList(String tplCategory)
+ {
+ List<String> templates = new ArrayList<String>();
+ templates.add("vm/java/domain.java.vm");
+ templates.add("vm/java/mapper.java.vm");
+ templates.add("vm/java/service.java.vm");
+ templates.add("vm/java/serviceImpl.java.vm");
+ templates.add("vm/java/controller.java.vm");
+ templates.add("vm/xml/mapper.xml.vm");
+ templates.add("vm/sql/sql.vm");
+ templates.add("vm/js/api.js.vm");
+ if (GenConstants.TPL_CRUD.equals(tplCategory))
+ {
+ templates.add("vm/vue/index.vue.vm");
+ }
+ else if (GenConstants.TPL_TREE.equals(tplCategory))
+ {
+ templates.add("vm/vue/index-tree.vue.vm");
+ }
+ return templates;
+ }
+
+ /**
+ * 获取文件名
+ */
+ public static String getFileName(String template, GenTable genTable)
+ {
+ // 文件名称
+ String fileName = "";
+ // 包路径
+ String packageName = genTable.getPackageName();
+ // 模块名
+ String moduleName = genTable.getModuleName();
+ // 大写类名
+ String className = genTable.getClassName();
+ // 业务名称
+ String businessName = genTable.getBusinessName();
+
+ String javaPath = PROJECT_PATH + "/" + StringUtils.replace(packageName, ".", "/");
+ String mybatisPath = MYBATIS_PATH + "/" + moduleName;
+ String vuePath = "vue";
+
+ if (template.contains("domain.java.vm"))
+ {
+ fileName = StringUtils.format("{}/domain/{}.java", javaPath, className);
+ }
+ else if (template.contains("mapper.java.vm"))
+ {
+ fileName = StringUtils.format("{}/mapper/{}Mapper.java", javaPath, className);
+ }
+ else if (template.contains("service.java.vm"))
+ {
+ fileName = StringUtils.format("{}/service/I{}Service.java", javaPath, className);
+ }
+ else if (template.contains("serviceImpl.java.vm"))
+ {
+ fileName = StringUtils.format("{}/service/impl/{}ServiceImpl.java", javaPath, className);
+ }
+ else if (template.contains("controller.java.vm"))
+ {
+ fileName = StringUtils.format("{}/controller/{}Controller.java", javaPath, className);
+ }
+ else if (template.contains("mapper.xml.vm"))
+ {
+ fileName = StringUtils.format("{}/{}Mapper.xml", mybatisPath, className);
+ }
+ else if (template.contains("sql.vm"))
+ {
+ fileName = businessName + "Menu.sql";
+ }
+ else if (template.contains("api.js.vm"))
+ {
+ fileName = StringUtils.format("{}/api/{}/{}.js", vuePath, moduleName, businessName);
+ }
+ else if (template.contains("index.vue.vm"))
+ {
+ fileName = StringUtils.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName);
+ }
+ else if (template.contains("index-tree.vue.vm"))
+ {
+ fileName = StringUtils.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName);
+ }
+ return fileName;
+ }
+
+ /**
+ * 获取包前缀
+ *
+ * @param packageName 包名称
+ * @return 包前缀名称
+ */
+ public static String getPackagePrefix(String packageName)
+ {
+ int lastIndex = packageName.lastIndexOf(".");
+ String basePackage = StringUtils.substring(packageName, 0, lastIndex);
+ return basePackage;
+ }
+
+ /**
+ * 根据列类型获取导入包
+ *
+ * @param column 列集合
+ * @return 返回需要导入的包列表
+ */
+ public static HashSet<String> getImportList(List<GenTableColumn> columns)
+ {
+ HashSet<String> importList = new HashSet<String>();
+ for (GenTableColumn column : columns)
+ {
+ if (!column.isSuperColumn() && GenConstants.TYPE_DATE.equals(column.getJavaType()))
+ {
+ importList.add("java.util.Date");
+ importList.add("com.fasterxml.jackson.annotation.JsonFormat");
+ }
+ else if (!column.isSuperColumn() && GenConstants.TYPE_BIGDECIMAL.equals(column.getJavaType()))
+ {
+ importList.add("java.math.BigDecimal");
+ }
+ }
+ return importList;
+ }
+
+ /**
+ * 获取权限前缀
+ *
+ * @param moduleName 模块名称
+ * @param businessName 业务名称
+ * @return 返回权限前缀
+ */
+ public static String getPermissionPrefix(String moduleName, String businessName)
+ {
+ return StringUtils.format("{}:{}", moduleName, businessName);
+
+ }
+
+ /**
+ * 获取树编码
+ *
+ * @param options 生成其他选项
+ * @return 树编码
+ */
+ public static String getTreecode(JSONObject paramsObj)
+ {
+ if (paramsObj.containsKey(GenConstants.TREE_CODE))
+ {
+ return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_CODE));
+ }
+ return "";
+ }
+
+ /**
+ * 获取树父编码
+ *
+ * @param options 生成其他选项
+ * @return 树父编码
+ */
+ public static String getTreeParentCode(JSONObject paramsObj)
+ {
+ if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE))
+ {
+ return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_PARENT_CODE));
+ }
+ return "";
+ }
+
+ /**
+ * 获取树名称
+ *
+ * @param options 生成其他选项
+ * @return 树名称
+ */
+ public static String getTreeName(JSONObject paramsObj)
+ {
+ if (paramsObj.containsKey(GenConstants.TREE_NAME))
+ {
+ return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_NAME));
+ }
+ return "";
+ }
+
+ /**
+ * 获取需要在哪一列上面显示展开按钮
+ *
+ * @param genTable 业务表对象
+ * @return 展开按钮列序号
+ */
+ public static int getExpandColumn(GenTable genTable)
+ {
+ String options = genTable.getOptions();
+ JSONObject paramsObj = JSONObject.parseObject(options);
+ String treeName = paramsObj.getString(GenConstants.TREE_NAME);
+ int num = 0;
+ for (GenTableColumn column : genTable.getColumns())
+ {
+ if (column.isList())
+ {
+ num++;
+ String columnName = column.getColumnName();
+ if (columnName.equals(treeName))
+ {
+ break;
+ }
+ }
+ }
+ return num;
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-gen/src/main/resources/banner.txt b/ruoyi-modules/ruoyi-gen/src/main/resources/banner.txt
new file mode 100644
index 0000000..a66e8b8
--- /dev/null
+++ b/ruoyi-modules/ruoyi-gen/src/main/resources/banner.txt
@@ -0,0 +1,10 @@
+Spring Boot Version: ${spring-boot.version}
+Spring Application Name: ${spring.application.name}
+ _
+ (_)
+ _ __ _ _ ___ _ _ _ ______ __ _ ___ _ __
+| '__|| | | | / _ \ | | | || ||______| / _` | / _ \| '_ \
+| | | |_| || (_) || |_| || | | (_| || __/| | | |
+|_| \__,_| \___/ \__, ||_| \__, | \___||_| |_|
+ __/ | __/ |
+ |___/ |___/
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-gen/src/main/resources/bootstrap.yml b/ruoyi-modules/ruoyi-gen/src/main/resources/bootstrap.yml
new file mode 100644
index 0000000..752cdbd
--- /dev/null
+++ b/ruoyi-modules/ruoyi-gen/src/main/resources/bootstrap.yml
@@ -0,0 +1,24 @@
+# Tomcat
+server:
+ port: 9202
+
+# Spring
+spring:
+ application:
+ # 应用名称
+ name: ruoyi-gen
+ profiles:
+ # 环境配置
+ active: dev
+ cloud:
+ nacos:
+ discovery:
+ # 服务注册地址
+ server-addr: 127.0.0.1:8848
+ config:
+ # 配置中心地址
+ server-addr: 127.0.0.1:8848
+ # 配置文件格式
+ file-extension: yml
+ # 共享配置
+ shared-dataids: application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
diff --git a/ruoyi-modules/ruoyi-gen/src/main/resources/logback.xml b/ruoyi-modules/ruoyi-gen/src/main/resources/logback.xml
new file mode 100644
index 0000000..bd4abac
--- /dev/null
+++ b/ruoyi-modules/ruoyi-gen/src/main/resources/logback.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration scan="true" scanPeriod="60 seconds" debug="false">
+ <!-- 日志存放路径 -->
+ <property name="log.path" value="/home/ruoyi/logs/ruoyi-gen" />
+ <!-- 日志输出格式 -->
+ <property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />:ss} %-5level ${springAppName:-} %thread %logger %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}/info.log</file>
+ <!-- 循环政策:基于时间创建日志文件 -->
+ <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+ <!-- 日志文件名格式 -->
+ <fileNamePattern>${log.path}/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}/error.log</file>
+ <!-- 循环政策:基于时间创建日志文件 -->
+ <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+ <!-- 日志文件名格式 -->
+ <fileNamePattern>${log.path}/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>
+
+ <!-- 系统模块日志级别控制 -->
+ <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>
+</configuration>
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-gen/src/main/resources/mapper/generator/GenTableColumnMapper.xml b/ruoyi-modules/ruoyi-gen/src/main/resources/mapper/generator/GenTableColumnMapper.xml
new file mode 100644
index 0000000..6a8b523
--- /dev/null
+++ b/ruoyi-modules/ruoyi-gen/src/main/resources/mapper/generator/GenTableColumnMapper.xml
@@ -0,0 +1,120 @@
+<?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.gen.mapper.GenTableColumnMapper">
+
+ <resultMap type="GenTableColumn" id="GenTableColumnResult">
+ <id property="columnId" column="column_id" />
+ <result property="tableId" column="table_id" />
+ <result property="columnName" column="column_name" />
+ <result property="columnComment" column="column_comment" />
+ <result property="columnType" column="column_type" />
+ <result property="javaType" column="java_type" />
+ <result property="javaField" column="java_field" />
+ <result property="isPk" column="is_pk" />
+ <result property="isIncrement" column="is_increment" />
+ <result property="isRequired" column="is_required" />
+ <result property="isInsert" column="is_insert" />
+ <result property="isEdit" column="is_edit" />
+ <result property="isList" column="is_list" />
+ <result property="isQuery" column="is_query" />
+ <result property="queryType" column="query_type" />
+ <result property="htmlType" column="html_type" />
+ <result property="dictType" column="dict_type" />
+ <result property="sort" column="sort" />
+ <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="selectGenTableColumnVo">
+ select column_id, table_id, column_name, column_comment, column_type, java_type, java_field, is_pk, is_increment, is_required, is_insert, is_edit, is_list, is_query, query_type, html_type, dict_type, sort, create_by, create_time, update_by, update_time from gen_table_column
+ </sql>
+
+ <select id="selectGenTableColumnListByTableId" parameterType="GenTableColumn" resultMap="GenTableColumnResult">
+ <include refid="selectGenTableColumnVo"/>
+ where table_id = #{tableId}
+ order by sort
+ </select>
+
+ <select id="selectDbTableColumnsByName" parameterType="String" resultMap="GenTableColumnResult">
+ select column_name, (case when (is_nullable = 'no' <![CDATA[ && ]]> column_key != 'PRI') then '1' else null end) as is_required, (case when column_key = 'PRI' then '1' else '0' end) as is_pk, ordinal_position as sort, column_comment, (case when extra = 'auto_increment' then '1' else '0' end) as is_increment, column_type
+ from information_schema.columns where table_schema = (select database()) and table_name = (#{tableName})
+ order by ordinal_position
+ </select>
+
+ <insert id="insertGenTableColumn" parameterType="GenTableColumn" useGeneratedKeys="true" keyProperty="columnId">
+ insert into gen_table_column (
+ <if test="tableId != null and tableId != ''">table_id,</if>
+ <if test="columnName != null and columnName != ''">column_name,</if>
+ <if test="columnComment != null and columnComment != ''">column_comment,</if>
+ <if test="columnType != null and columnType != ''">column_type,</if>
+ <if test="javaType != null and javaType != ''">java_type,</if>
+ <if test="javaField != null and javaField != ''">java_field,</if>
+ <if test="isPk != null and isPk != ''">is_pk,</if>
+ <if test="isIncrement != null and isIncrement != ''">is_increment,</if>
+ <if test="isRequired != null and isRequired != ''">is_required,</if>
+ <if test="isInsert != null and isInsert != ''">is_insert,</if>
+ <if test="isEdit != null and isEdit != ''">is_edit,</if>
+ <if test="isList != null and isList != ''">is_list,</if>
+ <if test="isQuery != null and isQuery != ''">is_query,</if>
+ <if test="queryType != null and queryType != ''">query_type,</if>
+ <if test="htmlType != null and htmlType != ''">html_type,</if>
+ <if test="dictType != null and dictType != ''">dict_type,</if>
+ <if test="sort != null">sort,</if>
+ <if test="createBy != null and createBy != ''">create_by,</if>
+ create_time
+ )values(
+ <if test="tableId != null and tableId != ''">#{tableId},</if>
+ <if test="columnName != null and columnName != ''">#{columnName},</if>
+ <if test="columnComment != null and columnComment != ''">#{columnComment},</if>
+ <if test="columnType != null and columnType != ''">#{columnType},</if>
+ <if test="javaType != null and javaType != ''">#{javaType},</if>
+ <if test="javaField != null and javaField != ''">#{javaField},</if>
+ <if test="isPk != null and isPk != ''">#{isPk},</if>
+ <if test="isIncrement != null and isIncrement != ''">#{isIncrement},</if>
+ <if test="isRequired != null and isRequired != ''">#{isRequired},</if>
+ <if test="isInsert != null and isInsert != ''">#{isInsert},</if>
+ <if test="isEdit != null and isEdit != ''">#{isEdit},</if>
+ <if test="isList != null and isList != ''">#{isList},</if>
+ <if test="isQuery != null and isQuery != ''">#{isQuery},</if>
+ <if test="queryType != null and queryType != ''">#{queryType},</if>
+ <if test="htmlType != null and htmlType != ''">#{htmlType},</if>
+ <if test="dictType != null and dictType != ''">#{dictType},</if>
+ <if test="sort != null">#{sort},</if>
+ <if test="createBy != null and createBy != ''">#{createBy},</if>
+ sysdate()
+ )
+ </insert>
+
+ <update id="updateGenTableColumn" parameterType="GenTableColumn">
+ update gen_table_column
+ <set>
+ column_comment = #{columnComment},
+ java_type = #{javaType},
+ java_field = #{javaField},
+ is_insert = #{isInsert},
+ is_edit = #{isEdit},
+ is_list = #{isList},
+ is_query = #{isQuery},
+ is_required = #{isRequired},
+ query_type = #{queryType},
+ html_type = #{htmlType},
+ dict_type = #{dictType},
+ sort = #{sort},
+ update_by = #{updateBy},
+ update_time = sysdate()
+ </set>
+ where column_id = #{columnId}
+ </update>
+
+ <delete id="deleteGenTableColumnByIds" parameterType="Long">
+ delete from gen_table_column where table_id in
+ <foreach collection="array" item="tableId" open="(" separator="," close=")">
+ #{tableId}
+ </foreach>
+ </delete>
+
+</mapper>
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-gen/src/main/resources/mapper/generator/GenTableMapper.xml b/ruoyi-modules/ruoyi-gen/src/main/resources/mapper/generator/GenTableMapper.xml
new file mode 100644
index 0000000..abb636e
--- /dev/null
+++ b/ruoyi-modules/ruoyi-gen/src/main/resources/mapper/generator/GenTableMapper.xml
@@ -0,0 +1,169 @@
+<?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.gen.mapper.GenTableMapper">
+
+ <resultMap type="GenTable" id="GenTableResult">
+ <id property="tableId" column="table_id" />
+ <result property="tableName" column="table_name" />
+ <result property="tableComment" column="table_comment" />
+ <result property="className" column="class_name" />
+ <result property="tplCategory" column="tpl_category" />
+ <result property="packageName" column="package_name" />
+ <result property="moduleName" column="module_name" />
+ <result property="businessName" column="business_name" />
+ <result property="functionName" column="function_name" />
+ <result property="functionAuthor" column="function_author" />
+ <result property="options" column="options" />
+ <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" />
+ <collection property="columns" javaType="java.util.List" resultMap="GenTableColumnResult" />
+ </resultMap>
+
+ <resultMap type="GenTableColumn" id="GenTableColumnResult">
+ <id property="columnId" column="column_id" />
+ <result property="tableId" column="table_id" />
+ <result property="columnName" column="column_name" />
+ <result property="columnComment" column="column_comment" />
+ <result property="columnType" column="column_type" />
+ <result property="javaType" column="java_type" />
+ <result property="javaField" column="java_field" />
+ <result property="isPk" column="is_pk" />
+ <result property="isIncrement" column="is_increment" />
+ <result property="isRequired" column="is_required" />
+ <result property="isInsert" column="is_insert" />
+ <result property="isEdit" column="is_edit" />
+ <result property="isList" column="is_list" />
+ <result property="isQuery" column="is_query" />
+ <result property="queryType" column="query_type" />
+ <result property="htmlType" column="html_type" />
+ <result property="dictType" column="dict_type" />
+ <result property="sort" column="sort" />
+ <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="selectGenTableVo">
+ select table_id, table_name, table_comment, class_name, tpl_category, package_name, module_name, business_name, function_name, function_author, options, create_by, create_time, update_by, update_time, remark from gen_table
+ </sql>
+
+ <select id="selectGenTableList" parameterType="GenTable" resultMap="GenTableResult">
+ <include refid="selectGenTableVo"/>
+ <where>
+ <if test="tableName != null and tableName != ''">
+ AND lower(table_name) like lower(concat('%', #{tableName}, '%'))
+ </if>
+ <if test="tableComment != null and tableComment != ''">
+ AND lower(table_comment) like lower(concat('%', #{tableComment}, '%'))
+ </if>
+ </where>
+ </select>
+
+ <select id="selectDbTableList" parameterType="GenTable" resultMap="GenTableResult">
+ select table_name, table_comment, create_time, update_time from information_schema.tables
+ where table_schema = (select database())
+ AND table_name NOT LIKE 'qrtz_%' AND table_name NOT LIKE 'gen_%'
+ AND table_name NOT IN (select table_name from gen_table)
+ <if test="tableName != null and tableName != ''">
+ AND lower(table_name) like lower(concat('%', #{tableName}, '%'))
+ </if>
+ <if test="tableComment != null and tableComment != ''">
+ AND lower(table_comment) like lower(concat('%', #{tableComment}, '%'))
+ </if>
+ </select>
+
+ <select id="selectDbTableListByNames" resultMap="GenTableResult">
+ select table_name, table_comment, create_time, update_time from information_schema.tables
+ where table_name NOT LIKE 'qrtz_%' and table_name NOT LIKE 'gen_%' and table_schema = (select database())
+ and table_name in
+ <foreach collection="array" item="name" open="(" separator="," close=")">
+ #{name}
+ </foreach>
+ </select>
+
+ <select id="selectTableByName" parameterType="String" resultMap="GenTableResult">
+ select table_name, table_comment, create_time, update_time from information_schema.tables
+ where table_comment <![CDATA[ <> ]]> '' and table_schema = (select database())
+ and table_name = #{tableName}
+ </select>
+
+ <select id="selectGenTableById" parameterType="Long" resultMap="GenTableResult">
+ SELECT t.table_id, t.table_name, t.table_comment, t.class_name, t.tpl_category, t.package_name, t.module_name, t.business_name, t.function_name, t.function_author, t.options, t.remark,
+ c.column_id, c.column_name, c.column_comment, c.column_type, c.java_type, c.java_field, c.is_pk, c.is_increment, c.is_required, c.is_insert, c.is_edit, c.is_list, c.is_query, c.query_type, c.html_type, c.dict_type, c.sort
+ FROM gen_table t
+ LEFT JOIN gen_table_column c ON t.table_id = c.table_id
+ where t.table_id = #{tableId} order by c.sort
+ </select>
+
+ <select id="selectGenTableByName" parameterType="String" resultMap="GenTableResult">
+ SELECT t.table_id, t.table_name, t.table_comment, t.class_name, t.tpl_category, t.package_name, t.module_name, t.business_name, t.function_name, t.function_author, t.options, t.remark,
+ c.column_id, c.column_name, c.column_comment, c.column_type, c.java_type, c.java_field, c.is_pk, c.is_increment, c.is_required, c.is_insert, c.is_edit, c.is_list, c.is_query, c.query_type, c.html_type, c.dict_type, c.sort
+ FROM gen_table t
+ LEFT JOIN gen_table_column c ON t.table_id = c.table_id
+ where t.table_name = #{tableName} order by c.sort
+ </select>
+
+ <insert id="insertGenTable" parameterType="GenTable" useGeneratedKeys="true" keyProperty="tableId">
+ insert into gen_table (
+ <if test="tableName != null">table_name,</if>
+ <if test="tableComment != null and tableComment != ''">table_comment,</if>
+ <if test="className != null and className != ''">class_name,</if>
+ <if test="tplCategory != null and tplCategory != ''">tpl_category,</if>
+ <if test="packageName != null and packageName != ''">package_name,</if>
+ <if test="moduleName != null and moduleName != ''">module_name,</if>
+ <if test="businessName != null and businessName != ''">business_name,</if>
+ <if test="functionName != null and functionName != ''">function_name,</if>
+ <if test="functionAuthor != null and functionAuthor != ''">function_author,</if>
+ <if test="remark != null and remark != ''">remark,</if>
+ <if test="createBy != null and createBy != ''">create_by,</if>
+ create_time
+ )values(
+ <if test="tableName != null">#{tableName},</if>
+ <if test="tableComment != null and tableComment != ''">#{tableComment},</if>
+ <if test="className != null and className != ''">#{className},</if>
+ <if test="tplCategory != null and tplCategory != ''">#{tplCategory},</if>
+ <if test="packageName != null and packageName != ''">#{packageName},</if>
+ <if test="moduleName != null and moduleName != ''">#{moduleName},</if>
+ <if test="businessName != null and businessName != ''">#{businessName},</if>
+ <if test="functionName != null and functionName != ''">#{functionName},</if>
+ <if test="functionAuthor != null and functionAuthor != ''">#{functionAuthor},</if>
+ <if test="remark != null and remark != ''">#{remark},</if>
+ <if test="createBy != null and createBy != ''">#{createBy},</if>
+ sysdate()
+ )
+ </insert>
+
+ <update id="updateGenTable" parameterType="GenTable">
+ update gen_table
+ <set>
+ <if test="tableName != null">table_name = #{tableName},</if>
+ <if test="tableComment != null and tableComment != ''">table_comment = #{tableComment},</if>
+ <if test="className != null and className != ''">class_name = #{className},</if>
+ <if test="functionAuthor != null and functionAuthor != ''">function_author = #{functionAuthor},</if>
+ <if test="tplCategory != null and tplCategory != ''">tpl_category = #{tplCategory},</if>
+ <if test="packageName != null and packageName != ''">package_name = #{packageName},</if>
+ <if test="moduleName != null and moduleName != ''">module_name = #{moduleName},</if>
+ <if test="businessName != null and businessName != ''">business_name = #{businessName},</if>
+ <if test="functionName != null and functionName != ''">function_name = #{functionName},</if>
+ <if test="options != null and options != ''">options = #{options},</if>
+ <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
+ <if test="remark != null">remark = #{remark},</if>
+ update_time = sysdate()
+ </set>
+ where table_id = #{tableId}
+ </update>
+
+ <delete id="deleteGenTableByIds" parameterType="Long">
+ delete from gen_table where table_id in
+ <foreach collection="array" item="tableId" open="(" separator="," close=")">
+ #{tableId}
+ </foreach>
+ </delete>
+
+</mapper>
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/controller.java.vm b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/controller.java.vm
new file mode 100644
index 0000000..c45ba66
--- /dev/null
+++ b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/controller.java.vm
@@ -0,0 +1,114 @@
+package ${packageName}.controller;
+
+import java.util.List;
+import org.springframework.security.access.prepost.PreAuthorize;
+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.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.ruoyi.framework.aspectj.lang.annotation.Log;
+import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
+import ${packageName}.domain.${ClassName};
+import ${packageName}.service.I${ClassName}Service;
+import com.ruoyi.framework.web.controller.BaseController;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+#if($table.crud)
+import com.ruoyi.framework.web.page.TableDataInfo;
+#elseif($table.tree)
+#end
+
+/**
+ * ${functionName}Controller
+ *
+ * @author ${author}
+ * @date ${datetime}
+ */
+@RestController
+@RequestMapping("/${moduleName}/${businessName}")
+public class ${ClassName}Controller extends BaseController
+{
+ @Autowired
+ private I${ClassName}Service ${className}Service;
+
+ /**
+ * 查询${functionName}列表
+ */
+ @PreAuthorize("@ss.hasPermi('${permissionPrefix}:list')")
+ @GetMapping("/list")
+#if($table.crud)
+ public TableDataInfo list(${ClassName} ${className})
+ {
+ startPage();
+ List<${ClassName}> list = ${className}Service.select${ClassName}List(${className});
+ return getDataTable(list);
+ }
+#elseif($table.tree)
+ public AjaxResult list(${ClassName} ${className})
+ {
+ List<${ClassName}> list = ${className}Service.select${ClassName}List(${className});
+ return AjaxResult.success(list);
+ }
+#end
+
+ /**
+ * 导出${functionName}列表
+ */
+ @PreAuthorize("@ss.hasPermi('${permissionPrefix}:export')")
+ @Log(title = "${functionName}", businessType = BusinessType.EXPORT)
+ @GetMapping("/export")
+ public AjaxResult export(${ClassName} ${className})
+ {
+ List<${ClassName}> list = ${className}Service.select${ClassName}List(${className});
+ ExcelUtil<${ClassName}> util = new ExcelUtil<${ClassName}>(${ClassName}.class);
+ return util.exportExcel(list, "${businessName}");
+ }
+
+ /**
+ * 获取${functionName}详细信息
+ */
+ @PreAuthorize("@ss.hasPermi('${permissionPrefix}:query')")
+ @GetMapping(value = "/{${pkColumn.javaField}}")
+ public AjaxResult getInfo(@PathVariable("${pkColumn.javaField}") ${pkColumn.javaType} ${pkColumn.javaField})
+ {
+ return AjaxResult.success(${className}Service.select${ClassName}ById(${pkColumn.javaField}));
+ }
+
+ /**
+ * 新增${functionName}
+ */
+ @PreAuthorize("@ss.hasPermi('${permissionPrefix}:add')")
+ @Log(title = "${functionName}", businessType = BusinessType.INSERT)
+ @PostMapping
+ public AjaxResult add(@RequestBody ${ClassName} ${className})
+ {
+ return toAjax(${className}Service.insert${ClassName}(${className}));
+ }
+
+ /**
+ * 修改${functionName}
+ */
+ @PreAuthorize("@ss.hasPermi('${permissionPrefix}:edit')")
+ @Log(title = "${functionName}", businessType = BusinessType.UPDATE)
+ @PutMapping
+ public AjaxResult edit(@RequestBody ${ClassName} ${className})
+ {
+ return toAjax(${className}Service.update${ClassName}(${className}));
+ }
+
+ /**
+ * 删除${functionName}
+ */
+ @PreAuthorize("@ss.hasPermi('${permissionPrefix}:remove')")
+ @Log(title = "${functionName}", businessType = BusinessType.DELETE)
+ @DeleteMapping("/{${pkColumn.javaField}s}")
+ public AjaxResult remove(@PathVariable ${pkColumn.javaType}[] ${pkColumn.javaField}s)
+ {
+ return toAjax(${className}Service.delete${ClassName}ByIds(${pkColumn.javaField}s));
+ }
+}
diff --git a/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/domain.java.vm b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/domain.java.vm
new file mode 100644
index 0000000..c943c1b
--- /dev/null
+++ b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/domain.java.vm
@@ -0,0 +1,85 @@
+package ${packageName}.domain;
+
+#foreach ($import in $importList)
+import ${import};
+#end
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.ruoyi.framework.aspectj.lang.annotation.Excel;
+#if($table.crud)
+import com.ruoyi.framework.web.domain.BaseEntity;
+#elseif($table.tree)
+import com.ruoyi.framework.web.domain.TreeEntity;
+#end
+
+/**
+ * ${functionName}对象 ${tableName}
+ *
+ * @author ${author}
+ * @date ${datetime}
+ */
+#if($table.crud)
+#set($Entity="BaseEntity")
+#elseif($table.tree)
+#set($Entity="TreeEntity")
+#end
+public class ${ClassName} extends ${Entity}
+{
+ private static final long serialVersionUID = 1L;
+
+#foreach ($column in $columns)
+#if(!$table.isSuperColumn($column.javaField))
+ /** $column.columnComment */
+#if($column.list)
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+#if($parentheseIndex != -1)
+ @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()")
+#elseif($column.javaType == 'Date')
+ @JsonFormat(pattern = "yyyy-MM-dd")
+ @Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd")
+#else
+ @Excel(name = "${comment}")
+#end
+#end
+ private $column.javaType $column.javaField;
+
+#end
+#end
+#foreach ($column in $columns)
+#if(!$table.isSuperColumn($column.javaField))
+#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]"))
+#set($AttrName=$column.javaField)
+#else
+#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
+#end
+ public void set${AttrName}($column.javaType $column.javaField)
+ {
+ this.$column.javaField = $column.javaField;
+ }
+
+ public $column.javaType get${AttrName}()
+ {
+ return $column.javaField;
+ }
+#end
+#end
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+#foreach ($column in $columns)
+#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]"))
+#set($AttrName=$column.javaField)
+#else
+#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
+#end
+ .append("${column.javaField}", get${AttrName}())
+#end
+ .toString();
+ }
+}
diff --git a/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/mapper.java.vm b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/mapper.java.vm
new file mode 100644
index 0000000..5502baf
--- /dev/null
+++ b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/mapper.java.vm
@@ -0,0 +1,61 @@
+package ${packageName}.mapper;
+
+import java.util.List;
+import ${packageName}.domain.${ClassName};
+
+/**
+ * ${functionName}Mapper接口
+ *
+ * @author ${author}
+ * @date ${datetime}
+ */
+public interface ${ClassName}Mapper
+{
+ /**
+ * 查询${functionName}
+ *
+ * @param ${pkColumn.javaField} ${functionName}ID
+ * @return ${functionName}
+ */
+ public ${ClassName} select${ClassName}ById(${pkColumn.javaType} ${pkColumn.javaField});
+
+ /**
+ * 查询${functionName}列表
+ *
+ * @param ${className} ${functionName}
+ * @return ${functionName}集合
+ */
+ public List<${ClassName}> select${ClassName}List(${ClassName} ${className});
+
+ /**
+ * 新增${functionName}
+ *
+ * @param ${className} ${functionName}
+ * @return 结果
+ */
+ public int insert${ClassName}(${ClassName} ${className});
+
+ /**
+ * 修改${functionName}
+ *
+ * @param ${className} ${functionName}
+ * @return 结果
+ */
+ public int update${ClassName}(${ClassName} ${className});
+
+ /**
+ * 删除${functionName}
+ *
+ * @param ${pkColumn.javaField} ${functionName}ID
+ * @return 结果
+ */
+ public int delete${ClassName}ById(${pkColumn.javaType} ${pkColumn.javaField});
+
+ /**
+ * 批量删除${functionName}
+ *
+ * @param ${pkColumn.javaField}s 需要删除的数据ID
+ * @return 结果
+ */
+ public int delete${ClassName}ByIds(${pkColumn.javaType}[] ${pkColumn.javaField}s);
+}
diff --git a/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/service.java.vm b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/service.java.vm
new file mode 100644
index 0000000..af422f3
--- /dev/null
+++ b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/service.java.vm
@@ -0,0 +1,61 @@
+package ${packageName}.service;
+
+import java.util.List;
+import ${packageName}.domain.${ClassName};
+
+/**
+ * ${functionName}Service接口
+ *
+ * @author ${author}
+ * @date ${datetime}
+ */
+public interface I${ClassName}Service
+{
+ /**
+ * 查询${functionName}
+ *
+ * @param ${pkColumn.javaField} ${functionName}ID
+ * @return ${functionName}
+ */
+ public ${ClassName} select${ClassName}ById(${pkColumn.javaType} ${pkColumn.javaField});
+
+ /**
+ * 查询${functionName}列表
+ *
+ * @param ${className} ${functionName}
+ * @return ${functionName}集合
+ */
+ public List<${ClassName}> select${ClassName}List(${ClassName} ${className});
+
+ /**
+ * 新增${functionName}
+ *
+ * @param ${className} ${functionName}
+ * @return 结果
+ */
+ public int insert${ClassName}(${ClassName} ${className});
+
+ /**
+ * 修改${functionName}
+ *
+ * @param ${className} ${functionName}
+ * @return 结果
+ */
+ public int update${ClassName}(${ClassName} ${className});
+
+ /**
+ * 批量删除${functionName}
+ *
+ * @param ${pkColumn.javaField}s 需要删除的${functionName}ID
+ * @return 结果
+ */
+ public int delete${ClassName}ByIds(${pkColumn.javaType}[] ${pkColumn.javaField}s);
+
+ /**
+ * 删除${functionName}信息
+ *
+ * @param ${pkColumn.javaField} ${functionName}ID
+ * @return 结果
+ */
+ public int delete${ClassName}ById(${pkColumn.javaType} ${pkColumn.javaField});
+}
diff --git a/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/serviceImpl.java.vm b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/serviceImpl.java.vm
new file mode 100644
index 0000000..05a3de8
--- /dev/null
+++ b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/serviceImpl.java.vm
@@ -0,0 +1,109 @@
+package ${packageName}.service.impl;
+
+import java.util.List;
+#foreach ($column in $columns)
+#if($column.javaField == 'createTime' || $column.javaField == 'updateTime')
+import com.ruoyi.common.utils.DateUtils;
+#break
+#end
+#end
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import ${packageName}.mapper.${ClassName}Mapper;
+import ${packageName}.domain.${ClassName};
+import ${packageName}.service.I${ClassName}Service;
+
+/**
+ * ${functionName}Service业务层处理
+ *
+ * @author ${author}
+ * @date ${datetime}
+ */
+@Service
+public class ${ClassName}ServiceImpl implements I${ClassName}Service
+{
+ @Autowired
+ private ${ClassName}Mapper ${className}Mapper;
+
+ /**
+ * 查询${functionName}
+ *
+ * @param ${pkColumn.javaField} ${functionName}ID
+ * @return ${functionName}
+ */
+ @Override
+ public ${ClassName} select${ClassName}ById(${pkColumn.javaType} ${pkColumn.javaField})
+ {
+ return ${className}Mapper.select${ClassName}ById(${pkColumn.javaField});
+ }
+
+ /**
+ * 查询${functionName}列表
+ *
+ * @param ${className} ${functionName}
+ * @return ${functionName}
+ */
+ @Override
+ public List<${ClassName}> select${ClassName}List(${ClassName} ${className})
+ {
+ return ${className}Mapper.select${ClassName}List(${className});
+ }
+
+ /**
+ * 新增${functionName}
+ *
+ * @param ${className} ${functionName}
+ * @return 结果
+ */
+ @Override
+ public int insert${ClassName}(${ClassName} ${className})
+ {
+#foreach ($column in $columns)
+#if($column.javaField == 'createTime')
+ ${className}.setCreateTime(DateUtils.getNowDate());
+#end
+#end
+ return ${className}Mapper.insert${ClassName}(${className});
+ }
+
+ /**
+ * 修改${functionName}
+ *
+ * @param ${className} ${functionName}
+ * @return 结果
+ */
+ @Override
+ public int update${ClassName}(${ClassName} ${className})
+ {
+#foreach ($column in $columns)
+#if($column.javaField == 'updateTime')
+ ${className}.setUpdateTime(DateUtils.getNowDate());
+#end
+#end
+ return ${className}Mapper.update${ClassName}(${className});
+ }
+
+ /**
+ * 批量删除${functionName}
+ *
+ * @param ${pkColumn.javaField}s 需要删除的${functionName}ID
+ * @return 结果
+ */
+ @Override
+ public int delete${ClassName}ByIds(${pkColumn.javaType}[] ${pkColumn.javaField}s)
+ {
+ return ${className}Mapper.delete${ClassName}ByIds(${pkColumn.javaField}s);
+ }
+
+ /**
+ * 删除${functionName}信息
+ *
+ * @param ${pkColumn.javaField} ${functionName}ID
+ * @return 结果
+ */
+ @Override
+ public int delete${ClassName}ById(${pkColumn.javaType} ${pkColumn.javaField})
+ {
+ return ${className}Mapper.delete${ClassName}ById(${pkColumn.javaField});
+ }
+}
diff --git a/ruoyi-modules/ruoyi-gen/src/main/resources/vm/js/api.js.vm b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/js/api.js.vm
new file mode 100644
index 0000000..cd2403c
--- /dev/null
+++ b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/js/api.js.vm
@@ -0,0 +1,53 @@
+import request from '@/utils/request'
+
+// 查询${functionName}列表
+export function list${BusinessName}(query) {
+ return request({
+ url: '/${moduleName}/${businessName}/list',
+ method: 'get',
+ params: query
+ })
+}
+
+// 查询${functionName}详细
+export function get${BusinessName}(${pkColumn.javaField}) {
+ return request({
+ url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField},
+ method: 'get'
+ })
+}
+
+// 新增${functionName}
+export function add${BusinessName}(data) {
+ return request({
+ url: '/${moduleName}/${businessName}',
+ method: 'post',
+ data: data
+ })
+}
+
+// 修改${functionName}
+export function update${BusinessName}(data) {
+ return request({
+ url: '/${moduleName}/${businessName}',
+ method: 'put',
+ data: data
+ })
+}
+
+// 删除${functionName}
+export function del${BusinessName}(${pkColumn.javaField}) {
+ return request({
+ url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField},
+ method: 'delete'
+ })
+}
+
+// 导出${functionName}
+export function export${BusinessName}(query) {
+ return request({
+ url: '/${moduleName}/${businessName}/export',
+ method: 'get',
+ params: query
+ })
+}
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-gen/src/main/resources/vm/sql/sql.vm b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/sql/sql.vm
new file mode 100644
index 0000000..c553476
--- /dev/null
+++ b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/sql/sql.vm
@@ -0,0 +1,22 @@
+-- 菜单 SQL
+insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+values('${functionName}', '3', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 'C', '0', '0', '${permissionPrefix}:list', '#', 'admin', '2018-03-01', 'ry', '2018-03-01', '${functionName}菜单');
+
+-- 按钮父菜单ID
+SELECT @parentId := LAST_INSERT_ID();
+
+-- 按钮 SQL
+insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+values('${functionName}查询', @parentId, '1', '#', '', 1, 'F', '0', '0', '${permissionPrefix}:query', '#', 'admin', '2018-03-01', 'ry', '2018-03-01', '');
+
+insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+values('${functionName}新增', @parentId, '2', '#', '', 1, 'F', '0', '0', '${permissionPrefix}:add', '#', 'admin', '2018-03-01', 'ry', '2018-03-01', '');
+
+insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+values('${functionName}修改', @parentId, '3', '#', '', 1, 'F', '0', '0', '${permissionPrefix}:edit', '#', 'admin', '2018-03-01', 'ry', '2018-03-01', '');
+
+insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+values('${functionName}删除', @parentId, '4', '#', '', 1, 'F', '0', '0', '${permissionPrefix}:remove', '#', 'admin', '2018-03-01', 'ry', '2018-03-01', '');
+
+insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+values('${functionName}导出', @parentId, '5', '#', '', 1, 'F', '0', '0', '${permissionPrefix}:export', '#', 'admin', '2018-03-01', 'ry', '2018-03-01', '');
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-gen/src/main/resources/vm/vue/index-tree.vue.vm b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/vue/index-tree.vue.vm
new file mode 100644
index 0000000..1a74fca
--- /dev/null
+++ b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/vue/index-tree.vue.vm
@@ -0,0 +1,411 @@
+<template>
+ <div class="app-container">
+ <el-form :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
+#foreach($column in $columns)
+#if($column.query)
+#set($dictType=$column.dictType)
+#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+#if($column.htmlType == "input")
+ <el-form-item label="${comment}" prop="${column.javaField}">
+ <el-input
+ v-model="queryParams.${column.javaField}"
+ placeholder="请输入${comment}"
+ clearable
+ size="small"
+ @keyup.enter.native="handleQuery"
+ />
+ </el-form-item>
+#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && "" != $dictType)
+ <el-form-item label="${comment}" prop="${column.javaField}">
+ <el-select v-model="queryParams.${column.javaField}" placeholder="请选择${comment}" clearable size="small">
+ <el-option
+ v-for="dict in ${column.javaField}Options"
+ :key="dict.dictValue"
+ :label="dict.dictLabel"
+ :value="dict.dictValue"
+ />
+ </el-select>
+ </el-form-item>
+#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && $dictType)
+ <el-form-item label="${comment}" prop="${column.javaField}">
+ <el-select v-model="queryParams.${column.javaField}" placeholder="请选择${comment}" clearable size="small">
+ <el-option label="请选择字典生成" value="" />
+ </el-select>
+ </el-form-item>
+#elseif($column.htmlType == "datetime")
+ <el-form-item label="${comment}" prop="${column.javaField}">
+ <el-date-picker clearable size="small" style="width: 200px"
+ v-model="queryParams.${column.javaField}"
+ type="date"
+ value-format="yyyy-MM-dd"
+ placeholder="选择${comment}">
+ </el-date-picker>
+ </el-form-item>
+#end
+#end
+#end
+ <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="['${moduleName}:${businessName}:add']"
+ >新增</el-button>
+ </el-form-item>
+ </el-form>
+
+ <el-table
+ v-loading="loading"
+ :data="${businessName}List"
+ row-key="${treeCode}"
+ default-expand-all
+ :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
+ >
+#foreach($column in $columns)
+#set($javaField=$column.javaField)
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+#if($column.pk)
+#elseif($column.list && $column.htmlType == "datetime")
+ <el-table-column label="${comment}" align="center" prop="${javaField}" width="180">
+ <template slot-scope="scope">
+ <span>{{ parseTime(scope.row.${javaField}, '{y}-{m}-{d}') }}</span>
+ </template>
+ </el-table-column>
+#elseif($column.list && "" != $column.dictType)
+ <el-table-column label="${comment}" align="center" prop="${javaField}" :formatter="${javaField}Format" />
+#elseif($column.list && "" != $javaField)
+ <el-table-column label="${comment}" align="center" prop="${javaField}" />
+#end
+#end
+ <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="['${moduleName}:${businessName}:edit']"
+ >修改</el-button>
+ <el-button
+ size="mini"
+ type="text"
+ icon="el-icon-delete"
+ @click="handleDelete(scope.row)"
+ v-hasPermi="['${moduleName}:${businessName}:remove']"
+ >删除</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+
+ <!-- 添加或修改${functionName}对话框 -->
+ <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+ <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+#foreach($column in $columns)
+#set($field=$column.javaField)
+#if($column.insert && !$column.pk)
+#if(($column.usableColumn) || (!$column.superColumn))
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+#set($dictType=$column.dictType)
+#if("" != $treeParentCode && $column.javaField == $treeParentCode)
+ <el-form-item label="${comment}" prop="${treeParentCode}">
+ <treeselect v-model="form.${treeParentCode}" :options="${businessName}Options" :normalizer="normalizer" placeholder="请选择${comment}" />
+ </el-form-item>
+#elseif($column.htmlType == "input")
+ <el-form-item label="${comment}" prop="${field}">
+ <el-input v-model="form.${field}" placeholder="请输入${comment}" />
+ </el-form-item>
+#elseif($column.htmlType == "select" && "" != $dictType)
+ <el-form-item label="${comment}">
+ <el-select v-model="form.${field}" placeholder="请选择${comment}">
+ <el-option
+ v-for="dict in ${field}Options"
+ :key="dict.dictValue"
+ :label="dict.dictLabel"
+ #if($column.javaType == "Integer"):value="parseInt(dict.dictValue)"#else:value="dict.dictValue"#end
+
+ ></el-option>
+ </el-select>
+ </el-form-item>
+#elseif($column.htmlType == "select" && $dictType)
+ <el-form-item label="${comment}">
+ <el-select v-model="form.${field}" placeholder="请选择${comment}">
+ <el-option label="请选择字典生成" value="" />
+ </el-select>
+ </el-form-item>
+#elseif($column.htmlType == "radio" && "" != $dictType)
+ <el-form-item label="${comment}">
+ <el-radio-group v-model="form.${field}">
+ <el-radio
+ v-for="dict in ${field}Options"
+ :key="dict.dictValue"
+ #if($column.javaType == "Integer"):label="parseInt(dict.dictValue)"#else:label="dict.dictValue"#end
+
+ >{{dict.dictLabel}}</el-radio>
+ </el-radio-group>
+ </el-form-item>
+#elseif($column.htmlType == "radio" && $dictType)
+ <el-form-item label="${comment}">
+ <el-radio-group v-model="form.${field}">
+ <el-radio label="1">请选择字典生成</el-radio>
+ </el-radio-group>
+ </el-form-item>
+#elseif($column.htmlType == "datetime")
+ <el-form-item label="${comment}" prop="${field}">
+ <el-date-picker clearable size="small" style="width: 200px"
+ v-model="form.${field}"
+ type="date"
+ value-format="yyyy-MM-dd"
+ placeholder="选择${comment}">
+ </el-date-picker>
+ </el-form-item>
+#elseif($column.htmlType == "textarea")
+ <el-form-item label="${comment}" prop="${field}">
+ <el-input v-model="form.${field}" type="textarea" placeholder="请输入内容" />
+ </el-form-item>
+#end
+#end
+#end
+#end
+ </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 { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName}, export${BusinessName} } from "@/api/${moduleName}/${businessName}";
+import Treeselect from "@riophae/vue-treeselect";
+import "@riophae/vue-treeselect/dist/vue-treeselect.css";
+
+export default {
+ name: "${BusinessName}",
+ components: { Treeselect },
+ data() {
+ return {
+ // 遮罩层
+ loading: true,
+ // ${functionName}表格数据
+ ${businessName}List: [],
+ // ${functionName}树选项
+ ${businessName}Options: [],
+ // 弹出层标题
+ title: "",
+ // 是否显示弹出层
+ open: false,
+#foreach ($column in $columns)
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+#if(${column.dictType} != '')
+ // $comment字典
+ ${column.javaField}Options: [],
+#end
+#end
+ // 查询参数
+ queryParams: {
+#foreach ($column in $columns)
+#if($column.query)
+ $column.javaField: undefined#if($velocityCount != $columns.size()),#end
+
+#end
+#end
+ },
+ // 表单参数
+ form: {},
+ // 表单校验
+ rules: {
+#foreach ($column in $columns)
+#if($column.required)
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+#set($comment=$column.columnComment)
+ $column.javaField: [
+ { required: true, message: "$comment不能为空", trigger: "blur" }
+ ]#if($velocityCount != $columns.size()),#end
+
+#end
+#end
+ }
+ };
+ },
+ created() {
+ this.getList();
+#foreach ($column in $columns)
+#if(${column.dictType} != '')
+ this.getDicts("${column.dictType}").then(response => {
+ this.${column.javaField}Options = response.data;
+ });
+#end
+#end
+ },
+ methods: {
+ /** 查询${functionName}列表 */
+ getList() {
+ this.loading = true;
+ list${BusinessName}(this.queryParams).then(response => {
+ this.${businessName}List = this.handleTree(response.data, "${treeCode}", "${treeParentCode}");
+ this.loading = false;
+ });
+ },
+ /** 转换${functionName}数据结构 */
+ normalizer(node) {
+ if (node.children && !node.children.length) {
+ delete node.children;
+ }
+ return {
+ id: node.${treeCode},
+ label: node.${treeName},
+ children: node.children
+ };
+ },
+ /** 查询部门下拉树结构 */
+ getTreeselect() {
+ list${BusinessName}().then(response => {
+ this.${businessName}Options = [];
+ const data = { ${treeCode}: 0, ${treeName}: '顶级节点', children: [] };
+ data.children = this.handleTree(response.data, "${treeCode}", "${treeParentCode}");
+ this.${businessName}Options.push(data);
+ });
+ },
+#foreach ($column in $columns)
+#if(${column.dictType} != '')
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+ // $comment字典翻译
+ ${column.javaField}Format(row, column) {
+ return this.selectDictLabel(this.${column.javaField}Options, row.${column.javaField});
+ },
+#end
+#end
+ // 取消按钮
+ cancel() {
+ this.open = false;
+ this.reset();
+ },
+ // 表单重置
+ reset() {
+ this.form = {
+#foreach ($column in $columns)
+#if($column.htmlType == "radio")
+ $column.javaField: "0"#if($velocityCount != $columns.size()),#end
+
+#else
+ $column.javaField: undefined#if($velocityCount != $columns.size()),#end
+
+#end
+#end
+ };
+ this.resetForm("form");
+ },
+ /** 搜索按钮操作 */
+ handleQuery() {
+ this.getList();
+ },
+ /** 重置按钮操作 */
+ resetQuery() {
+ this.resetForm("queryForm");
+ this.handleQuery();
+ },
+ /** 新增按钮操作 */
+ handleAdd() {
+ this.reset();
+ this.getTreeselect();
+ this.open = true;
+ this.title = "添加${functionName}";
+ },
+ /** 修改按钮操作 */
+ handleUpdate(row) {
+ this.reset();
+ this.getTreeselect();
+ if (row != undefined) {
+ this.form.${treeParentCode} = row.${treeCode};
+ }
+ get${BusinessName}(row.${pkColumn.javaField}).then(response => {
+ this.form = response.data;
+ this.open = true;
+ this.title = "修改${functionName}";
+ });
+ },
+ /** 提交按钮 */
+ submitForm: function() {
+ this.#[[$]]#refs["form"].validate(valid => {
+ if (valid) {
+ if (this.form.${pkColumn.javaField} != undefined) {
+ update${BusinessName}(this.form).then(response => {
+ if (response.code === 200) {
+ this.msgSuccess("修改成功");
+ this.open = false;
+ this.getList();
+ } else {
+ this.msgError(response.msg);
+ }
+ });
+ } else {
+ add${BusinessName}(this.form).then(response => {
+ if (response.code === 200) {
+ this.msgSuccess("新增成功");
+ this.open = false;
+ this.getList();
+ } else {
+ this.msgError(response.msg);
+ }
+ });
+ }
+ }
+ });
+ },
+ /** 删除按钮操作 */
+ handleDelete(row) {
+ this.$confirm('是否确认删除${functionName}编号为"' + row.${pkColumn.javaField} + '"的数据项?', "警告", {
+ confirmButtonText: "确定",
+ cancelButtonText: "取消",
+ type: "warning"
+ }).then(function() {
+ return del${BusinessName}(row.${pkColumn.javaField});
+ }).then(() => {
+ this.getList();
+ this.msgSuccess("删除成功");
+ }).catch(function() {});
+ }
+ }
+};
+</script>
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-gen/src/main/resources/vm/vue/index.vue.vm b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/vue/index.vue.vm
new file mode 100644
index 0000000..7c19a3a
--- /dev/null
+++ b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/vue/index.vue.vm
@@ -0,0 +1,442 @@
+<template>
+ <div class="app-container">
+ <el-form :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
+#foreach($column in $columns)
+#if($column.query)
+#set($dictType=$column.dictType)
+#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+#if($column.htmlType == "input")
+ <el-form-item label="${comment}" prop="${column.javaField}">
+ <el-input
+ v-model="queryParams.${column.javaField}"
+ placeholder="请输入${comment}"
+ clearable
+ size="small"
+ @keyup.enter.native="handleQuery"
+ />
+ </el-form-item>
+#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && "" != $dictType)
+ <el-form-item label="${comment}" prop="${column.javaField}">
+ <el-select v-model="queryParams.${column.javaField}" placeholder="请选择${comment}" clearable size="small">
+ <el-option
+ v-for="dict in ${column.javaField}Options"
+ :key="dict.dictValue"
+ :label="dict.dictLabel"
+ :value="dict.dictValue"
+ />
+ </el-select>
+ </el-form-item>
+#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && $dictType)
+ <el-form-item label="${comment}" prop="${column.javaField}">
+ <el-select v-model="queryParams.${column.javaField}" placeholder="请选择${comment}" clearable size="small">
+ <el-option label="请选择字典生成" value="" />
+ </el-select>
+ </el-form-item>
+#elseif($column.htmlType == "datetime")
+ <el-form-item label="${comment}" prop="${column.javaField}">
+ <el-date-picker clearable size="small" style="width: 200px"
+ v-model="queryParams.${column.javaField}"
+ type="date"
+ value-format="yyyy-MM-dd"
+ placeholder="选择${comment}">
+ </el-date-picker>
+ </el-form-item>
+#end
+#end
+#end
+ <el-form-item>
+ <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+ <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+ </el-form-item>
+ </el-form>
+
+ <el-row :gutter="10" class="mb8">
+ <el-col :span="1.5">
+ <el-button
+ type="primary"
+ icon="el-icon-plus"
+ size="mini"
+ @click="handleAdd"
+ v-hasPermi="['${moduleName}:${businessName}:add']"
+ >新增</el-button>
+ </el-col>
+ <el-col :span="1.5">
+ <el-button
+ type="success"
+ icon="el-icon-edit"
+ size="mini"
+ :disabled="single"
+ @click="handleUpdate"
+ v-hasPermi="['${moduleName}:${businessName}:edit']"
+ >修改</el-button>
+ </el-col>
+ <el-col :span="1.5">
+ <el-button
+ type="danger"
+ icon="el-icon-delete"
+ size="mini"
+ :disabled="multiple"
+ @click="handleDelete"
+ v-hasPermi="['${moduleName}:${businessName}:remove']"
+ >删除</el-button>
+ </el-col>
+ <el-col :span="1.5">
+ <el-button
+ type="warning"
+ icon="el-icon-download"
+ size="mini"
+ @click="handleExport"
+ v-hasPermi="['${moduleName}:${businessName}:export']"
+ >导出</el-button>
+ </el-col>
+ </el-row>
+
+ <el-table v-loading="loading" :data="${businessName}List" @selection-change="handleSelectionChange">
+ <el-table-column type="selection" width="55" align="center" />
+#foreach($column in $columns)
+#set($javaField=$column.javaField)
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+#if($column.pk)
+ <el-table-column label="${comment}" align="center" prop="${javaField}" />
+#elseif($column.list && $column.htmlType == "datetime")
+ <el-table-column label="${comment}" align="center" prop="${javaField}" width="180">
+ <template slot-scope="scope">
+ <span>{{ parseTime(scope.row.${javaField}, '{y}-{m}-{d}') }}</span>
+ </template>
+ </el-table-column>
+#elseif($column.list && "" != $column.dictType)
+ <el-table-column label="${comment}" align="center" prop="${javaField}" :formatter="${javaField}Format" />
+#elseif($column.list && "" != $javaField)
+ <el-table-column label="${comment}" align="center" prop="${javaField}" />
+#end
+#end
+ <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="['${moduleName}:${businessName}:edit']"
+ >修改</el-button>
+ <el-button
+ size="mini"
+ type="text"
+ icon="el-icon-delete"
+ @click="handleDelete(scope.row)"
+ v-hasPermi="['${moduleName}:${businessName}: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"
+ />
+
+ <!-- 添加或修改${functionName}对话框 -->
+ <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+ <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+#foreach($column in $columns)
+#set($field=$column.javaField)
+#if($column.insert && !$column.pk)
+#if(($column.usableColumn) || (!$column.superColumn))
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+#set($dictType=$column.dictType)
+#if($column.htmlType == "input")
+ <el-form-item label="${comment}" prop="${field}">
+ <el-input v-model="form.${field}" placeholder="请输入${comment}" />
+ </el-form-item>
+#elseif($column.htmlType == "select" && "" != $dictType)
+ <el-form-item label="${comment}">
+ <el-select v-model="form.${field}" placeholder="请选择${comment}">
+ <el-option
+ v-for="dict in ${field}Options"
+ :key="dict.dictValue"
+ :label="dict.dictLabel"
+ #if($column.javaType == "Integer"):value="parseInt(dict.dictValue)"#else:value="dict.dictValue"#end
+
+ ></el-option>
+ </el-select>
+ </el-form-item>
+#elseif($column.htmlType == "select" && $dictType)
+ <el-form-item label="${comment}">
+ <el-select v-model="form.${field}" placeholder="请选择${comment}">
+ <el-option label="请选择字典生成" value="" />
+ </el-select>
+ </el-form-item>
+#elseif($column.htmlType == "radio" && "" != $dictType)
+ <el-form-item label="${comment}">
+ <el-radio-group v-model="form.${field}">
+ <el-radio
+ v-for="dict in ${field}Options"
+ :key="dict.dictValue"
+ #if($column.javaType == "Integer"):label="parseInt(dict.dictValue)"#else:label="dict.dictValue"#end
+
+ >{{dict.dictLabel}}</el-radio>
+ </el-radio-group>
+ </el-form-item>
+#elseif($column.htmlType == "radio" && $dictType)
+ <el-form-item label="${comment}">
+ <el-radio-group v-model="form.${field}">
+ <el-radio label="1">请选择字典生成</el-radio>
+ </el-radio-group>
+ </el-form-item>
+#elseif($column.htmlType == "datetime")
+ <el-form-item label="${comment}" prop="${field}">
+ <el-date-picker clearable size="small" style="width: 200px"
+ v-model="form.${field}"
+ type="date"
+ value-format="yyyy-MM-dd"
+ placeholder="选择${comment}">
+ </el-date-picker>
+ </el-form-item>
+#elseif($column.htmlType == "textarea")
+ <el-form-item label="${comment}" prop="${field}">
+ <el-input v-model="form.${field}" type="textarea" placeholder="请输入内容" />
+ </el-form-item>
+#end
+#end
+#end
+#end
+ </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 { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName}, export${BusinessName} } from "@/api/${moduleName}/${businessName}";
+
+export default {
+ name: "${BusinessName}",
+ data() {
+ return {
+ // 遮罩层
+ loading: true,
+ // 选中数组
+ ids: [],
+ // 非单个禁用
+ single: true,
+ // 非多个禁用
+ multiple: true,
+ // 总条数
+ total: 0,
+ // ${functionName}表格数据
+ ${businessName}List: [],
+ // 弹出层标题
+ title: "",
+ // 是否显示弹出层
+ open: false,
+#foreach ($column in $columns)
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+#if(${column.dictType} != '')
+ // $comment字典
+ ${column.javaField}Options: [],
+#end
+#end
+ // 查询参数
+ queryParams: {
+ pageNum: 1,
+ pageSize: 10,
+#foreach ($column in $columns)
+#if($column.query)
+ $column.javaField: undefined#if($velocityCount != $columns.size()),#end
+
+#end
+#end
+ },
+ // 表单参数
+ form: {},
+ // 表单校验
+ rules: {
+#foreach ($column in $columns)
+#if($column.required)
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+#set($comment=$column.columnComment)
+ $column.javaField: [
+ { required: true, message: "$comment不能为空", trigger: "blur" }
+ ]#if($velocityCount != $columns.size()),#end
+
+#end
+#end
+ }
+ };
+ },
+ created() {
+ this.getList();
+#foreach ($column in $columns)
+#if(${column.dictType} != '')
+ this.getDicts("${column.dictType}").then(response => {
+ this.${column.javaField}Options = response.data;
+ });
+#end
+#end
+ },
+ methods: {
+ /** 查询${functionName}列表 */
+ getList() {
+ this.loading = true;
+ list${BusinessName}(this.queryParams).then(response => {
+ this.${businessName}List = response.rows;
+ this.total = response.total;
+ this.loading = false;
+ });
+ },
+#foreach ($column in $columns)
+#if(${column.dictType} != '')
+#set($parentheseIndex=$column.columnComment.indexOf("("))
+#if($parentheseIndex != -1)
+#set($comment=$column.columnComment.substring(0, $parentheseIndex))
+#else
+#set($comment=$column.columnComment)
+#end
+ // $comment字典翻译
+ ${column.javaField}Format(row, column) {
+ return this.selectDictLabel(this.${column.javaField}Options, row.${column.javaField});
+ },
+#end
+#end
+ // 取消按钮
+ cancel() {
+ this.open = false;
+ this.reset();
+ },
+ // 表单重置
+ reset() {
+ this.form = {
+#foreach ($column in $columns)
+#if($column.htmlType == "radio")
+ $column.javaField: "0"#if($velocityCount != $columns.size()),#end
+
+#else
+ $column.javaField: undefined#if($velocityCount != $columns.size()),#end
+
+#end
+#end
+ };
+ this.resetForm("form");
+ },
+ /** 搜索按钮操作 */
+ handleQuery() {
+ this.queryParams.pageNum = 1;
+ this.getList();
+ },
+ /** 重置按钮操作 */
+ resetQuery() {
+ this.resetForm("queryForm");
+ this.handleQuery();
+ },
+ // 多选框选中数据
+ handleSelectionChange(selection) {
+ this.ids = selection.map(item => item.${pkColumn.javaField})
+ this.single = selection.length!=1
+ this.multiple = !selection.length
+ },
+ /** 新增按钮操作 */
+ handleAdd() {
+ this.reset();
+ this.open = true;
+ this.title = "添加${functionName}";
+ },
+ /** 修改按钮操作 */
+ handleUpdate(row) {
+ this.reset();
+ const ${pkColumn.javaField} = row.${pkColumn.javaField} || this.ids
+ get${BusinessName}(${pkColumn.javaField}).then(response => {
+ this.form = response.data;
+ this.open = true;
+ this.title = "修改${functionName}";
+ });
+ },
+ /** 提交按钮 */
+ submitForm: function() {
+ this.#[[$]]#refs["form"].validate(valid => {
+ if (valid) {
+ if (this.form.${pkColumn.javaField} != undefined) {
+ update${BusinessName}(this.form).then(response => {
+ if (response.code === 200) {
+ this.msgSuccess("修改成功");
+ this.open = false;
+ this.getList();
+ } else {
+ this.msgError(response.msg);
+ }
+ });
+ } else {
+ add${BusinessName}(this.form).then(response => {
+ if (response.code === 200) {
+ this.msgSuccess("新增成功");
+ this.open = false;
+ this.getList();
+ } else {
+ this.msgError(response.msg);
+ }
+ });
+ }
+ }
+ });
+ },
+ /** 删除按钮操作 */
+ handleDelete(row) {
+ const ${pkColumn.javaField}s = row.${pkColumn.javaField} || this.ids;
+ this.$confirm('是否确认删除${functionName}编号为"' + ${pkColumn.javaField}s + '"的数据项?', "警告", {
+ confirmButtonText: "确定",
+ cancelButtonText: "取消",
+ type: "warning"
+ }).then(function() {
+ return del${BusinessName}(${pkColumn.javaField}s);
+ }).then(() => {
+ this.getList();
+ this.msgSuccess("删除成功");
+ }).catch(function() {});
+ },
+ /** 导出按钮操作 */
+ handleExport() {
+ const queryParams = this.queryParams;
+ this.$confirm('是否确认导出所有${functionName}数据项?', "警告", {
+ confirmButtonText: "确定",
+ cancelButtonText: "取消",
+ type: "warning"
+ }).then(function() {
+ return export${BusinessName}(queryParams);
+ }).then(response => {
+ this.download(response.msg);
+ }).catch(function() {});
+ }
+ }
+};
+</script>
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-gen/src/main/resources/vm/xml/mapper.xml.vm b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/xml/mapper.xml.vm
new file mode 100644
index 0000000..ef9d682
--- /dev/null
+++ b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/xml/mapper.xml.vm
@@ -0,0 +1,95 @@
+<?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="${packageName}.mapper.${ClassName}Mapper">
+
+ <resultMap type="${ClassName}" id="${ClassName}Result">
+#foreach ($column in $columns)
+ <result property="${column.javaField}" column="${column.columnName}" />
+#end
+ </resultMap>
+
+ <sql id="select${ClassName}Vo">
+ select#foreach($column in $columns) $column.columnName#if($velocityCount != $columns.size()),#end#end from ${tableName}
+ </sql>
+
+ <select id="select${ClassName}List" parameterType="${ClassName}" resultMap="${ClassName}Result">
+ <include refid="select${ClassName}Vo"/>
+ <where>
+#foreach($column in $columns)
+#set($queryType=$column.queryType)
+#set($javaField=$column.javaField)
+#set($javaType=$column.javaType)
+#set($columnName=$column.columnName)
+#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
+#if($column.query)
+#if($column.queryType == "EQ")
+ <if test="$javaField != null #if($javaType == 'String' ) and $javaField.trim() != ''#end"> and $columnName = #{$javaField}</if>
+#elseif($queryType == "NE")
+ <if test="$javaField != null #if($javaType == 'String' ) and $javaField.trim() != ''#end"> and $columnName != #{$javaField}</if>
+#elseif($queryType == "GT")
+ <if test="$javaField != null #if($javaType == 'String' ) and $javaField.trim() != ''#end"> and $columnName > #{$javaField}</if>
+#elseif($queryType == "GTE")
+ <if test="$javaField != null #if($javaType == 'String' ) and $javaField.trim() != ''#end"> and $columnName >= #{$javaField}</if>
+#elseif($queryType == "LT")
+ <if test="$javaField != null #if($javaType == 'String' ) and $javaField.trim() != ''#end"> and $columnName < #{$javaField}</if>
+#elseif($queryType == "LTE")
+ <if test="$javaField != null #if($javaType == 'String' ) and $javaField.trim() != ''#end"> and $columnName <= #{$javaField}</if>
+#elseif($queryType == "LIKE")
+ <if test="$javaField != null #if($javaType == 'String' ) and $javaField.trim() != ''#end"> and $columnName like concat('%', #{$javaField}, '%')</if>
+#elseif($queryType == "BETWEEN")
+ <if test="params.begin$AttrName != null and params.begin$AttrName != '' and params.end$AttrName != null and params.end$AttrName != ''"> and $columnName between #{params.begin$AttrName} and #{params.end$AttrName}</if>
+#end
+#end
+#end
+ </where>
+ </select>
+
+ <select id="select${ClassName}ById" parameterType="${pkColumn.javaType}" resultMap="${ClassName}Result">
+ <include refid="select${ClassName}Vo"/>
+ where ${pkColumn.columnName} = #{${pkColumn.javaField}}
+ </select>
+
+ <insert id="insert${ClassName}" parameterType="${ClassName}"#if($pkColumn.increment) useGeneratedKeys="true" keyProperty="$pkColumn.javaField"#end>
+ insert into ${tableName}
+ <trim prefix="(" suffix=")" suffixOverrides=",">
+#foreach($column in $columns)
+#if($column.columnName != $pkColumn.columnName || !$pkColumn.increment)
+ <if test="$column.javaField != null #if($column.javaType == 'String' ) and $column.javaField != ''#end">$column.columnName,</if>
+#end
+#end
+ </trim>
+ <trim prefix="values (" suffix=")" suffixOverrides=",">
+#foreach($column in $columns)
+#if($column.columnName != $pkColumn.columnName || !$pkColumn.increment)
+ <if test="$column.javaField != null #if($column.javaType == 'String' ) and $column.javaField != ''#end">#{$column.javaField},</if>
+#end
+#end
+ </trim>
+ </insert>
+
+ <update id="update${ClassName}" parameterType="${ClassName}">
+ update ${tableName}
+ <trim prefix="SET" suffixOverrides=",">
+#foreach($column in $columns)
+#if($column.columnName != $pkColumn.columnName)
+ <if test="$column.javaField != null #if($column.javaType == 'String' ) and $column.javaField != ''#end">$column.columnName = #{$column.javaField},</if>
+#end
+#end
+ </trim>
+ where ${pkColumn.columnName} = #{${pkColumn.javaField}}
+ </update>
+
+ <delete id="delete${ClassName}ById" parameterType="${pkColumn.javaType}">
+ delete from ${tableName} where ${pkColumn.columnName} = #{${pkColumn.javaField}}
+ </delete>
+
+ <delete id="delete${ClassName}ByIds" parameterType="String">
+ delete from ${tableName} where ${pkColumn.columnName} in
+ <foreach item="${pkColumn.javaField}" collection="array" open="(" separator="," close=")">
+ #{${pkColumn.javaField}}
+ </foreach>
+ </delete>
+
+</mapper>
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-job/pom.xml b/ruoyi-modules/ruoyi-job/pom.xml
new file mode 100644
index 0000000..6a0a65e
--- /dev/null
+++ b/ruoyi-modules/ruoyi-job/pom.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns="http://maven.apache.org/POM/4.0.0"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-modules</artifactId>
+ <version>1.0.0</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>ruoyi-modules-job</artifactId>
+
+ <description>
+ ruoyi-modules-job定时任务
+ </description>
+
+ <dependencies>
+
+ <!-- SpringCloud Ailibaba Nacos -->
+ <dependency>
+ <groupId>com.alibaba.cloud</groupId>
+ <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
+ </dependency>
+
+ <!-- SpringCloud Ailibaba Nacos Config -->
+ <dependency>
+ <groupId>com.alibaba.cloud</groupId>
+ <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
+ </dependency>
+
+ <!-- SpringCloud Netflix Hystrix -->
+ <dependency>
+ <groupId>org.springframework.cloud</groupId>
+ <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
+ </dependency>
+
+ <!-- SpringBoot Web -->
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-web</artifactId>
+ </dependency>
+
+ <!-- Swagger -->
+ <dependency>
+ <groupId>io.springfox</groupId>
+ <artifactId>springfox-swagger-ui</artifactId>
+ <version>${swagger.fox.version}</version>
+ </dependency>
+
+ <!-- Quartz -->
+ <dependency>
+ <groupId>org.quartz-scheduler</groupId>
+ <artifactId>quartz</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>com.mchange</groupId>
+ <artifactId>c3p0</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+ <!-- Mysql Connector -->
+ <dependency>
+ <groupId>mysql</groupId>
+ <artifactId>mysql-connector-java</artifactId>
+ </dependency>
+
+ <!-- Ruoyi Common Security-->
+ <dependency>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-common-security</artifactId>
+ </dependency>
+
+ <!-- Ruoyi Common Log -->
+ <dependency>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-common-log</artifactId>
+ </dependency>
+
+ <!-- Ruoyi Common Swagger -->
+ <dependency>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-common-swagger</artifactId>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>repackage</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/RuoYiJobApplication.java b/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/RuoYiJobApplication.java
new file mode 100644
index 0000000..15a3912
--- /dev/null
+++ b/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/RuoYiJobApplication.java
@@ -0,0 +1,34 @@
+package com.ruoyi.job;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.cloud.client.SpringCloudApplication;
+import com.ruoyi.common.security.annotation.EnableCustomConfig;
+import com.ruoyi.common.security.annotation.EnableRyFeignClients;
+import com.ruoyi.common.swagger.annotation.EnableCustomSwagger2;
+
+/**
+ * 定时任务
+ *
+ * @author ruoyi
+ */
+@EnableCustomConfig
+@EnableCustomSwagger2
+@EnableRyFeignClients
+@SpringCloudApplication
+public class RuoYiJobApplication
+{
+ public static void main(String[] args)
+ {
+ SpringApplication.run(RuoYiJobApplication.class, args);
+ System.out.println("(♥◠‿◠)ノ゙ 定时任务模块启动成功 ლ(´ڡ`ლ)゙ \n" +
+ " .-------. ____ __ \n" +
+ " | _ _ \\ \\ \\ / / \n" +
+ " | ( ' ) | \\ _. / ' \n" +
+ " |(_ o _) / _( )_ .' \n" +
+ " | (_,_).' __ ___(_ o _)' \n" +
+ " | |\\ \\ | || |(_,_)' \n" +
+ " | | \\ `' /| `-' / \n" +
+ " | | \\ / \\ / \n" +
+ " ''-' `'-' `-..-' ");
+ }
+}
diff --git a/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/config/ScheduleConfig.java b/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/config/ScheduleConfig.java
new file mode 100644
index 0000000..a7ad163
--- /dev/null
+++ b/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/config/ScheduleConfig.java
@@ -0,0 +1,57 @@
+package com.ruoyi.job.config;
+
+import java.util.Properties;
+import javax.sql.DataSource;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.quartz.SchedulerFactoryBean;
+
+/**
+ * 定时任务配置
+ *
+ * @author ruoyi
+ */
+@Configuration
+public class ScheduleConfig
+{
+ @Bean
+ public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource)
+ {
+ SchedulerFactoryBean factory = new SchedulerFactoryBean();
+ factory.setDataSource(dataSource);
+
+ // quartz参数
+ Properties prop = new Properties();
+ prop.put("org.quartz.scheduler.instanceName", "RuoyiScheduler");
+ prop.put("org.quartz.scheduler.instanceId", "AUTO");
+ // 线程池配置
+ prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
+ prop.put("org.quartz.threadPool.threadCount", "20");
+ prop.put("org.quartz.threadPool.threadPriority", "5");
+ // JobStore配置
+ prop.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
+ // 集群配置
+ prop.put("org.quartz.jobStore.isClustered", "true");
+ prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000");
+ prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1");
+ prop.put("org.quartz.jobStore.txIsolationLevelSerializable", "true");
+
+ // sqlserver 启用
+ // prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?");
+ prop.put("org.quartz.jobStore.misfireThreshold", "12000");
+ prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
+ factory.setQuartzProperties(prop);
+
+ factory.setSchedulerName("RuoyiScheduler");
+ // 延时启动
+ factory.setStartupDelay(1);
+ factory.setApplicationContextSchedulerContextKey("applicationContextKey");
+ // 可选,QuartzScheduler
+ // 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了
+ factory.setOverwriteExistingJobs(true);
+ // 设置自动启动,默认为true
+ factory.setAutoStartup(true);
+
+ return factory;
+ }
+}
diff --git a/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/controller/SysJobController.java b/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/controller/SysJobController.java
new file mode 100644
index 0000000..1287678
--- /dev/null
+++ b/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/controller/SysJobController.java
@@ -0,0 +1,132 @@
+package com.ruoyi.job.controller;
+
+import java.io.IOException;
+import java.util.List;
+import javax.servlet.http.HttpServletResponse;
+import org.quartz.SchedulerException;
+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.core.exception.job.TaskException;
+import com.ruoyi.common.core.utils.poi.ExcelUtil;
+import com.ruoyi.common.core.web.controller.BaseController;
+import com.ruoyi.common.core.web.domain.AjaxResult;
+import com.ruoyi.common.core.web.page.TableDataInfo;
+import com.ruoyi.common.log.annotation.Log;
+import com.ruoyi.common.log.enums.BusinessType;
+import com.ruoyi.job.domain.SysJob;
+import com.ruoyi.job.service.ISysJobService;
+
+/**
+ * 调度任务信息操作处理
+ *
+ * @author ruoyi
+ */
+@RestController
+@RequestMapping("/job")
+public class SysJobController extends BaseController
+{
+ @Autowired
+ private ISysJobService jobService;
+
+ /**
+ * 查询定时任务列表
+ */
+ @PreAuthorize("@ss.hasPermi('monitor:job:list')")
+ @GetMapping("/list")
+ public TableDataInfo list(SysJob sysJob)
+ {
+ startPage();
+ List<SysJob> list = jobService.selectJobList(sysJob);
+ return getDataTable(list);
+ }
+
+ /**
+ * 导出定时任务列表
+ */
+ @PreAuthorize("@ss.hasPermi('monitor:job:export')")
+ @Log(title = "定时任务", businessType = BusinessType.EXPORT)
+ @PostMapping("/export")
+ public void export(HttpServletResponse response, SysJob sysJob) throws IOException
+ {
+ List<SysJob> list = jobService.selectJobList(sysJob);
+ ExcelUtil<SysJob> util = new ExcelUtil<SysJob>(SysJob.class);
+ util.exportExcel(response, list, "定时任务");
+ }
+
+ /**
+ * 获取定时任务详细信息
+ */
+ @PreAuthorize("@ss.hasPermi('monitor:job:query')")
+ @GetMapping(value = "/{jobId}")
+ public AjaxResult getInfo(@PathVariable("jobId") Long jobId)
+ {
+ return AjaxResult.success(jobService.selectJobById(jobId));
+ }
+
+ /**
+ * 新增定时任务
+ */
+ @PreAuthorize("@ss.hasPermi('monitor:job:add')")
+ @Log(title = "定时任务", businessType = BusinessType.INSERT)
+ @PostMapping
+ public AjaxResult add(@RequestBody SysJob sysJob) throws SchedulerException, TaskException
+ {
+ return toAjax(jobService.insertJob(sysJob));
+ }
+
+ /**
+ * 修改定时任务
+ */
+ @PreAuthorize("@ss.hasPermi('monitor:job:edit')")
+ @Log(title = "定时任务", businessType = BusinessType.UPDATE)
+ @PutMapping
+ public AjaxResult edit(@RequestBody SysJob sysJob) throws SchedulerException, TaskException
+ {
+ return toAjax(jobService.updateJob(sysJob));
+ }
+
+ /**
+ * 定时任务状态修改
+ */
+ @PreAuthorize("@ss.hasPermi('monitor:job:changeStatus')")
+ @Log(title = "定时任务", businessType = BusinessType.UPDATE)
+ @PutMapping("/changeStatus")
+ public AjaxResult changeStatus(@RequestBody SysJob job) throws SchedulerException
+ {
+ SysJob newJob = jobService.selectJobById(job.getJobId());
+ newJob.setStatus(job.getStatus());
+ return toAjax(jobService.changeStatus(newJob));
+ }
+
+ /**
+ * 定时任务立即执行一次
+ */
+ @PreAuthorize("@ss.hasPermi('monitor:job:changeStatus')")
+ @Log(title = "定时任务", businessType = BusinessType.UPDATE)
+ @PutMapping("/run")
+ public AjaxResult run(@RequestBody SysJob job) throws SchedulerException
+ {
+ jobService.run(job);
+ return AjaxResult.success();
+ }
+
+ /**
+ * 删除定时任务
+ */
+ @PreAuthorize("@ss.hasPermi('monitor:job:remove')")
+ @Log(title = "定时任务", businessType = BusinessType.DELETE)
+ @DeleteMapping("/{jobIds}")
+ public AjaxResult remove(@PathVariable Long[] jobIds) throws SchedulerException, TaskException
+ {
+ jobService.deleteJobByIds(jobIds);
+ return AjaxResult.success();
+ }
+}
diff --git a/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/controller/SysJobLogController.java b/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/controller/SysJobLogController.java
new file mode 100644
index 0000000..5d5dd35
--- /dev/null
+++ b/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/controller/SysJobLogController.java
@@ -0,0 +1,92 @@
+package com.ruoyi.job.controller;
+
+import java.io.IOException;
+import java.util.List;
+import javax.servlet.http.HttpServletResponse;
+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.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.ruoyi.common.core.utils.poi.ExcelUtil;
+import com.ruoyi.common.core.web.controller.BaseController;
+import com.ruoyi.common.core.web.domain.AjaxResult;
+import com.ruoyi.common.core.web.page.TableDataInfo;
+import com.ruoyi.common.log.annotation.Log;
+import com.ruoyi.common.log.enums.BusinessType;
+import com.ruoyi.job.domain.SysJobLog;
+import com.ruoyi.job.service.ISysJobLogService;
+
+/**
+ * 调度日志操作处理
+ *
+ * @author ruoyi
+ */
+@RestController
+@RequestMapping("/job/log")
+public class SysJobLogController extends BaseController
+{
+ @Autowired
+ private ISysJobLogService jobLogService;
+
+ /**
+ * 查询定时任务调度日志列表
+ */
+ @PreAuthorize("@ss.hasPermi('monitor:job:list')")
+ @GetMapping("/list")
+ public TableDataInfo list(SysJobLog sysJobLog)
+ {
+ startPage();
+ List<SysJobLog> list = jobLogService.selectJobLogList(sysJobLog);
+ return getDataTable(list);
+ }
+
+ /**
+ * 导出定时任务调度日志列表
+ */
+ @PreAuthorize("@ss.hasPermi('monitor:job:export')")
+ @Log(title = "任务调度日志", businessType = BusinessType.EXPORT)
+ @PostMapping("/export")
+ public void export(HttpServletResponse response, SysJobLog sysJobLog) throws IOException
+ {
+ List<SysJobLog> list = jobLogService.selectJobLogList(sysJobLog);
+ ExcelUtil<SysJobLog> util = new ExcelUtil<SysJobLog>(SysJobLog.class);
+ util.exportExcel(response, list, "调度日志");
+ }
+
+ /**
+ * 根据调度编号获取详细信息
+ */
+ @PreAuthorize("@ss.hasPermi('monitor:job:query')")
+ @GetMapping(value = "/{configId}")
+ public AjaxResult getInfo(@PathVariable Long jobLogId)
+ {
+ return AjaxResult.success(jobLogService.selectJobLogById(jobLogId));
+ }
+
+ /**
+ * 删除定时任务调度日志
+ */
+ @PreAuthorize("@ss.hasPermi('monitor:job:remove')")
+ @Log(title = "定时任务调度日志", businessType = BusinessType.DELETE)
+ @DeleteMapping("/{jobLogIds}")
+ public AjaxResult remove(@PathVariable Long[] jobLogIds)
+ {
+ return toAjax(jobLogService.deleteJobLogByIds(jobLogIds));
+ }
+
+ /**
+ * 清空定时任务调度日志
+ */
+ @PreAuthorize("@ss.hasPermi('monitor:job:remove')")
+ @Log(title = "调度日志", businessType = BusinessType.CLEAN)
+ @DeleteMapping("/clean")
+ public AjaxResult clean()
+ {
+ jobLogService.cleanJobLog();
+ return AjaxResult.success();
+ }
+}
diff --git a/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/domain/SysJob.java b/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/domain/SysJob.java
new file mode 100644
index 0000000..aaca764
--- /dev/null
+++ b/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/domain/SysJob.java
@@ -0,0 +1,172 @@
+package com.ruoyi.job.domain;
+
+import java.io.Serializable;
+import java.util.Date;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Size;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.common.core.annotation.Excel;
+import com.ruoyi.common.core.annotation.Excel.ColumnType;
+import com.ruoyi.common.core.constant.ScheduleConstants;
+import com.ruoyi.common.core.utils.StringUtils;
+import com.ruoyi.common.core.web.domain.BaseEntity;
+import com.ruoyi.job.util.CronUtils;
+
+/**
+ * 定时任务调度表 sys_job
+ *
+ * @author ruoyi
+ */
+public class SysJob extends BaseEntity implements Serializable
+{
+ private static final long serialVersionUID = 1L;
+
+ /** 任务ID */
+ @Excel(name = "任务序号", cellType = ColumnType.NUMERIC)
+ private Long jobId;
+
+ /** 任务名称 */
+ @Excel(name = "任务名称")
+ private String jobName;
+
+ /** 任务组名 */
+ @Excel(name = "任务组名")
+ private String jobGroup;
+
+ /** 调用目标字符串 */
+ @Excel(name = "调用目标字符串")
+ private String invokeTarget;
+
+ /** cron执行表达式 */
+ @Excel(name = "执行表达式 ")
+ private String cronExpression;
+
+ /** cron计划策略 */
+ @Excel(name = "计划策略 ", readConverterExp = "0=默认,1=立即触发执行,2=触发一次执行,3=不触发立即执行")
+ private String misfirePolicy = ScheduleConstants.MISFIRE_DEFAULT;
+
+ /** 是否并发执行(0允许 1禁止) */
+ @Excel(name = "并发执行", readConverterExp = "0=允许,1=禁止")
+ private String concurrent;
+
+ /** 任务状态(0正常 1暂停) */
+ @Excel(name = "任务状态", readConverterExp = "0=正常,1=暂停")
+ private String status;
+
+ public Long getJobId()
+ {
+ return jobId;
+ }
+
+ public void setJobId(Long jobId)
+ {
+ this.jobId = jobId;
+ }
+
+ @NotBlank(message = "任务名称不能为空")
+ @Size(min = 0, max = 64, message = "任务名称不能超过64个字符")
+ public String getJobName()
+ {
+ return jobName;
+ }
+
+ public void setJobName(String jobName)
+ {
+ this.jobName = jobName;
+ }
+
+ public String getJobGroup()
+ {
+ return jobGroup;
+ }
+
+ public void setJobGroup(String jobGroup)
+ {
+ this.jobGroup = jobGroup;
+ }
+
+ @NotBlank(message = "调用目标字符串不能为空")
+ @Size(min = 0, max = 1000, message = "调用目标字符串长度不能超过500个字符")
+ public String getInvokeTarget()
+ {
+ return invokeTarget;
+ }
+
+ public void setInvokeTarget(String invokeTarget)
+ {
+ this.invokeTarget = invokeTarget;
+ }
+
+ @NotBlank(message = "Cron执行表达式不能为空")
+ @Size(min = 0, max = 255, message = "Cron执行表达式不能超过255个字符")
+ public String getCronExpression()
+ {
+ return cronExpression;
+ }
+
+ public void setCronExpression(String cronExpression)
+ {
+ this.cronExpression = cronExpression;
+ }
+
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ public Date getNextValidTime()
+ {
+ if (StringUtils.isNotEmpty(cronExpression))
+ {
+ return CronUtils.getNextExecution(cronExpression);
+ }
+ return null;
+ }
+
+ public String getMisfirePolicy()
+ {
+ return misfirePolicy;
+ }
+
+ public void setMisfirePolicy(String misfirePolicy)
+ {
+ this.misfirePolicy = misfirePolicy;
+ }
+
+ public String getConcurrent()
+ {
+ return concurrent;
+ }
+
+ public void setConcurrent(String concurrent)
+ {
+ this.concurrent = concurrent;
+ }
+
+ public String getStatus()
+ {
+ return status;
+ }
+
+ public void setStatus(String status)
+ {
+ this.status = status;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+ .append("jobId", getJobId())
+ .append("jobName", getJobName())
+ .append("jobGroup", getJobGroup())
+ .append("cronExpression", getCronExpression())
+ .append("nextValidTime", getNextValidTime())
+ .append("misfirePolicy", getMisfirePolicy())
+ .append("concurrent", getConcurrent())
+ .append("status", getStatus())
+ .append("createBy", getCreateBy())
+ .append("createTime", getCreateTime())
+ .append("updateBy", getUpdateBy())
+ .append("updateTime", getUpdateTime())
+ .append("remark", getRemark())
+ .toString();
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/domain/SysJobLog.java b/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/domain/SysJobLog.java
new file mode 100644
index 0000000..ec863c9
--- /dev/null
+++ b/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/domain/SysJobLog.java
@@ -0,0 +1,155 @@
+package com.ruoyi.job.domain;
+
+import java.util.Date;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.ruoyi.common.core.annotation.Excel;
+import com.ruoyi.common.core.web.domain.BaseEntity;
+
+/**
+ * 定时任务调度日志表 sys_job_log
+ *
+ * @author ruoyi
+ */
+public class SysJobLog extends BaseEntity
+{
+ private static final long serialVersionUID = 1L;
+
+ /** ID */
+ @Excel(name = "日志序号")
+ private Long jobLogId;
+
+ /** 任务名称 */
+ @Excel(name = "任务名称")
+ private String jobName;
+
+ /** 任务组名 */
+ @Excel(name = "任务组名")
+ private String jobGroup;
+
+ /** 调用目标字符串 */
+ @Excel(name = "调用目标字符串")
+ private String invokeTarget;
+
+ /** 日志信息 */
+ @Excel(name = "日志信息")
+ private String jobMessage;
+
+ /** 执行状态(0正常 1失败) */
+ @Excel(name = "执行状态", readConverterExp = "0=正常,1=失败")
+ private String status;
+
+ /** 异常信息 */
+ @Excel(name = "异常信息")
+ private String exceptionInfo;
+
+ /** 开始时间 */
+ private Date startTime;
+
+ /** 停止时间 */
+ private Date stopTime;
+
+ public Long getJobLogId()
+ {
+ return jobLogId;
+ }
+
+ public void setJobLogId(Long jobLogId)
+ {
+ this.jobLogId = jobLogId;
+ }
+
+ public String getJobName()
+ {
+ return jobName;
+ }
+
+ public void setJobName(String jobName)
+ {
+ this.jobName = jobName;
+ }
+
+ public String getJobGroup()
+ {
+ return jobGroup;
+ }
+
+ public void setJobGroup(String jobGroup)
+ {
+ this.jobGroup = jobGroup;
+ }
+
+ public String getInvokeTarget()
+ {
+ return invokeTarget;
+ }
+
+ public void setInvokeTarget(String invokeTarget)
+ {
+ this.invokeTarget = invokeTarget;
+ }
+
+ public String getJobMessage()
+ {
+ return jobMessage;
+ }
+
+ public void setJobMessage(String jobMessage)
+ {
+ this.jobMessage = jobMessage;
+ }
+
+ public String getStatus()
+ {
+ return status;
+ }
+
+ public void setStatus(String status)
+ {
+ this.status = status;
+ }
+
+ public String getExceptionInfo()
+ {
+ return exceptionInfo;
+ }
+
+ public void setExceptionInfo(String exceptionInfo)
+ {
+ this.exceptionInfo = exceptionInfo;
+ }
+
+ public Date getStartTime()
+ {
+ return startTime;
+ }
+
+ public void setStartTime(Date startTime)
+ {
+ this.startTime = startTime;
+ }
+
+ public Date getStopTime()
+ {
+ return stopTime;
+ }
+
+ public void setStopTime(Date stopTime)
+ {
+ this.stopTime = stopTime;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+ .append("jobLogId", getJobLogId())
+ .append("jobName", getJobName())
+ .append("jobGroup", getJobGroup())
+ .append("jobMessage", getJobMessage())
+ .append("status", getStatus())
+ .append("exceptionInfo", getExceptionInfo())
+ .append("startTime", getStartTime())
+ .append("stopTime", getStopTime())
+ .toString();
+ }
+}
diff --git a/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/mapper/SysJobLogMapper.java b/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/mapper/SysJobLogMapper.java
new file mode 100644
index 0000000..742eba5
--- /dev/null
+++ b/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/mapper/SysJobLogMapper.java
@@ -0,0 +1,64 @@
+package com.ruoyi.job.mapper;
+
+import java.util.List;
+import com.ruoyi.job.domain.SysJobLog;
+
+/**
+ * 调度任务日志信息 数据层
+ *
+ * @author ruoyi
+ */
+public interface SysJobLogMapper
+{
+ /**
+ * 获取quartz调度器日志的计划任务
+ *
+ * @param jobLog 调度日志信息
+ * @return 调度任务日志集合
+ */
+ public List<SysJobLog> selectJobLogList(SysJobLog jobLog);
+
+ /**
+ * 查询所有调度任务日志
+ *
+ * @return 调度任务日志列表
+ */
+ public List<SysJobLog> selectJobLogAll();
+
+ /**
+ * 通过调度任务日志ID查询调度信息
+ *
+ * @param jobLogId 调度任务日志ID
+ * @return 调度任务日志对象信息
+ */
+ public SysJobLog selectJobLogById(Long jobLogId);
+
+ /**
+ * 新增任务日志
+ *
+ * @param jobLog 调度日志信息
+ * @return 结果
+ */
+ public int insertJobLog(SysJobLog jobLog);
+
+ /**
+ * 批量删除调度日志信息
+ *
+ * @param logIds 需要删除的数据ID
+ * @return 结果
+ */
+ public int deleteJobLogByIds(Long[] logIds);
+
+ /**
+ * 删除任务日志
+ *
+ * @param jobId 调度日志ID
+ * @return 结果
+ */
+ public int deleteJobLogById(Long jobId);
+
+ /**
+ * 清空任务日志
+ */
+ public void cleanJobLog();
+}
diff --git a/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/mapper/SysJobMapper.java b/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/mapper/SysJobMapper.java
new file mode 100644
index 0000000..352fa87
--- /dev/null
+++ b/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/mapper/SysJobMapper.java
@@ -0,0 +1,67 @@
+package com.ruoyi.job.mapper;
+
+import java.util.List;
+import com.ruoyi.job.domain.SysJob;
+
+/**
+ * 调度任务信息 数据层
+ *
+ * @author ruoyi
+ */
+public interface SysJobMapper
+{
+ /**
+ * 查询调度任务日志集合
+ *
+ * @param job 调度信息
+ * @return 操作日志集合
+ */
+ public List<SysJob> selectJobList(SysJob job);
+
+ /**
+ * 查询所有调度任务
+ *
+ * @return 调度任务列表
+ */
+ public List<SysJob> selectJobAll();
+
+ /**
+ * 通过调度ID查询调度任务信息
+ *
+ * @param jobId 调度ID
+ * @return 角色对象信息
+ */
+ public SysJob selectJobById(Long jobId);
+
+ /**
+ * 通过调度ID删除调度任务信息
+ *
+ * @param jobId 调度ID
+ * @return 结果
+ */
+ public int deleteJobById(Long jobId);
+
+ /**
+ * 批量删除调度任务信息
+ *
+ * @param ids 需要删除的数据ID
+ * @return 结果
+ */
+ public int deleteJobByIds(Long[] ids);
+
+ /**
+ * 修改调度任务信息
+ *
+ * @param job 调度任务信息
+ * @return 结果
+ */
+ public int updateJob(SysJob job);
+
+ /**
+ * 新增调度任务信息
+ *
+ * @param job 调度任务信息
+ * @return 结果
+ */
+ public int insertJob(SysJob job);
+}
diff --git a/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/service/ISysJobLogService.java b/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/service/ISysJobLogService.java
new file mode 100644
index 0000000..0e804e2
--- /dev/null
+++ b/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/service/ISysJobLogService.java
@@ -0,0 +1,56 @@
+package com.ruoyi.job.service;
+
+import java.util.List;
+import com.ruoyi.job.domain.SysJobLog;
+
+/**
+ * 定时任务调度日志信息信息 服务层
+ *
+ * @author ruoyi
+ */
+public interface ISysJobLogService
+{
+ /**
+ * 获取quartz调度器日志的计划任务
+ *
+ * @param jobLog 调度日志信息
+ * @return 调度任务日志集合
+ */
+ public List<SysJobLog> selectJobLogList(SysJobLog jobLog);
+
+ /**
+ * 通过调度任务日志ID查询调度信息
+ *
+ * @param jobLogId 调度任务日志ID
+ * @return 调度任务日志对象信息
+ */
+ public SysJobLog selectJobLogById(Long jobLogId);
+
+ /**
+ * 新增任务日志
+ *
+ * @param jobLog 调度日志信息
+ */
+ public void addJobLog(SysJobLog jobLog);
+
+ /**
+ * 批量删除调度日志信息
+ *
+ * @param logIds 需要删除的日志ID
+ * @return 结果
+ */
+ public int deleteJobLogByIds(Long[] logIds);
+
+ /**
+ * 删除任务日志
+ *
+ * @param jobId 调度日志ID
+ * @return 结果
+ */
+ public int deleteJobLogById(Long jobId);
+
+ /**
+ * 清空任务日志
+ */
+ public void cleanJobLog();
+}
diff --git a/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/service/ISysJobService.java b/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/service/ISysJobService.java
new file mode 100644
index 0000000..ba17269
--- /dev/null
+++ b/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/service/ISysJobService.java
@@ -0,0 +1,102 @@
+package com.ruoyi.job.service;
+
+import java.util.List;
+import org.quartz.SchedulerException;
+import com.ruoyi.common.core.exception.job.TaskException;
+import com.ruoyi.job.domain.SysJob;
+
+/**
+ * 定时任务调度信息信息 服务层
+ *
+ * @author ruoyi
+ */
+public interface ISysJobService
+{
+ /**
+ * 获取quartz调度器的计划任务
+ *
+ * @param job 调度信息
+ * @return 调度任务集合
+ */
+ public List<SysJob> selectJobList(SysJob job);
+
+ /**
+ * 通过调度任务ID查询调度信息
+ *
+ * @param jobId 调度任务ID
+ * @return 调度任务对象信息
+ */
+ public SysJob selectJobById(Long jobId);
+
+ /**
+ * 暂停任务
+ *
+ * @param job 调度信息
+ * @return 结果
+ */
+ public int pauseJob(SysJob job) throws SchedulerException;
+
+ /**
+ * 恢复任务
+ *
+ * @param job 调度信息
+ * @return 结果
+ */
+ public int resumeJob(SysJob job) throws SchedulerException;
+
+ /**
+ * 删除任务后,所对应的trigger也将被删除
+ *
+ * @param job 调度信息
+ * @return 结果
+ */
+ public int deleteJob(SysJob job) throws SchedulerException;
+
+ /**
+ * 批量删除调度信息
+ *
+ * @param jobIds 需要删除的任务ID
+ * @return 结果
+ */
+ public void deleteJobByIds(Long[] jobIds) throws SchedulerException;
+
+ /**
+ * 任务调度状态修改
+ *
+ * @param job 调度信息
+ * @return 结果
+ */
+ public int changeStatus(SysJob job) throws SchedulerException;
+
+ /**
+ * 立即运行任务
+ *
+ * @param job 调度信息
+ * @return 结果
+ */
+ public void run(SysJob job) throws SchedulerException;
+
+ /**
+ * 新增任务
+ *
+ * @param job 调度信息
+ * @return 结果
+ */
+ public int insertJob(SysJob job) throws SchedulerException, TaskException;
+
+ /**
+ * 更新任务
+ *
+ * @param job 调度信息
+ * @return 结果
+ */
+ public int updateJob(SysJob job) throws SchedulerException, TaskException;
+
+ /**
+ * 校验cron表达式是否有效
+ *
+ * @param cronExpression 表达式
+ * @return 结果
+ */
+ public boolean checkCronExpressionIsValid(String cronExpression);
+}
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/service/SysJobLogServiceImpl.java b/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/service/SysJobLogServiceImpl.java
new file mode 100644
index 0000000..0886f31
--- /dev/null
+++ b/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/service/SysJobLogServiceImpl.java
@@ -0,0 +1,86 @@
+package com.ruoyi.job.service;
+
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.ruoyi.job.domain.SysJobLog;
+import com.ruoyi.job.mapper.SysJobLogMapper;
+
+/**
+ * 定时任务调度日志信息 服务层
+ *
+ * @author ruoyi
+ */
+@Service
+public class SysJobLogServiceImpl implements ISysJobLogService
+{
+ @Autowired
+ private SysJobLogMapper jobLogMapper;
+
+ /**
+ * 获取quartz调度器日志的计划任务
+ *
+ * @param jobLog 调度日志信息
+ * @return 调度任务日志集合
+ */
+ @Override
+ public List<SysJobLog> selectJobLogList(SysJobLog jobLog)
+ {
+ return jobLogMapper.selectJobLogList(jobLog);
+ }
+
+ /**
+ * 通过调度任务日志ID查询调度信息
+ *
+ * @param jobLogId 调度任务日志ID
+ * @return 调度任务日志对象信息
+ */
+ @Override
+ public SysJobLog selectJobLogById(Long jobLogId)
+ {
+ return jobLogMapper.selectJobLogById(jobLogId);
+ }
+
+ /**
+ * 新增任务日志
+ *
+ * @param jobLog 调度日志信息
+ */
+ @Override
+ public void addJobLog(SysJobLog jobLog)
+ {
+ jobLogMapper.insertJobLog(jobLog);
+ }
+
+ /**
+ * 批量删除调度日志信息
+ *
+ * @param logIds 需要删除的数据ID
+ * @return 结果
+ */
+ @Override
+ public int deleteJobLogByIds(Long[] logIds)
+ {
+ return jobLogMapper.deleteJobLogByIds(logIds);
+ }
+
+ /**
+ * 删除任务日志
+ *
+ * @param jobId 调度日志ID
+ */
+ @Override
+ public int deleteJobLogById(Long jobId)
+ {
+ return jobLogMapper.deleteJobLogById(jobId);
+ }
+
+ /**
+ * 清空任务日志
+ */
+ @Override
+ public void cleanJobLog()
+ {
+ jobLogMapper.cleanJobLog();
+ }
+}
diff --git a/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/service/SysJobServiceImpl.java b/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/service/SysJobServiceImpl.java
new file mode 100644
index 0000000..4699b3d
--- /dev/null
+++ b/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/service/SysJobServiceImpl.java
@@ -0,0 +1,253 @@
+package com.ruoyi.job.service;
+
+import java.util.List;
+import javax.annotation.PostConstruct;
+import org.quartz.JobDataMap;
+import org.quartz.JobKey;
+import org.quartz.Scheduler;
+import org.quartz.SchedulerException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import com.ruoyi.common.core.constant.ScheduleConstants;
+import com.ruoyi.common.core.exception.job.TaskException;
+import com.ruoyi.job.domain.SysJob;
+import com.ruoyi.job.mapper.SysJobMapper;
+import com.ruoyi.job.util.CronUtils;
+import com.ruoyi.job.util.ScheduleUtils;
+
+/**
+ * 定时任务调度信息 服务层
+ *
+ * @author ruoyi
+ */
+@Service
+public class SysJobServiceImpl implements ISysJobService
+{
+ @Autowired
+ private Scheduler scheduler;
+
+ @Autowired
+ private SysJobMapper jobMapper;
+
+ /**
+ * 项目启动时,初始化定时器 主要是防止手动修改数据库导致未同步到定时任务处理(注:不能手动修改数据库ID和任务组名,否则会导致脏数据)
+ */
+ @PostConstruct
+ public void init() throws SchedulerException, TaskException
+ {
+ scheduler.clear();
+ List<SysJob> jobList = jobMapper.selectJobAll();
+ for (SysJob job : jobList)
+ {
+ ScheduleUtils.createScheduleJob(scheduler, job);
+ }
+ }
+
+ /**
+ * 获取quartz调度器的计划任务列表
+ *
+ * @param job 调度信息
+ * @return
+ */
+ @Override
+ public List<SysJob> selectJobList(SysJob job)
+ {
+ return jobMapper.selectJobList(job);
+ }
+
+ /**
+ * 通过调度任务ID查询调度信息
+ *
+ * @param jobId 调度任务ID
+ * @return 调度任务对象信息
+ */
+ @Override
+ public SysJob selectJobById(Long jobId)
+ {
+ return jobMapper.selectJobById(jobId);
+ }
+
+ /**
+ * 暂停任务
+ *
+ * @param job 调度信息
+ */
+ @Override
+ @Transactional
+ public int pauseJob(SysJob job) throws SchedulerException
+ {
+ Long jobId = job.getJobId();
+ String jobGroup = job.getJobGroup();
+ job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
+ int rows = jobMapper.updateJob(job);
+ if (rows > 0)
+ {
+ scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup));
+ }
+ return rows;
+ }
+
+ /**
+ * 恢复任务
+ *
+ * @param job 调度信息
+ */
+ @Override
+ @Transactional
+ public int resumeJob(SysJob job) throws SchedulerException
+ {
+ Long jobId = job.getJobId();
+ String jobGroup = job.getJobGroup();
+ job.setStatus(ScheduleConstants.Status.NORMAL.getValue());
+ int rows = jobMapper.updateJob(job);
+ if (rows > 0)
+ {
+ scheduler.resumeJob(ScheduleUtils.getJobKey(jobId, jobGroup));
+ }
+ return rows;
+ }
+
+ /**
+ * 删除任务后,所对应的trigger也将被删除
+ *
+ * @param job 调度信息
+ */
+ @Override
+ @Transactional
+ public int deleteJob(SysJob job) throws SchedulerException
+ {
+ Long jobId = job.getJobId();
+ String jobGroup = job.getJobGroup();
+ int rows = jobMapper.deleteJobById(jobId);
+ if (rows > 0)
+ {
+ scheduler.deleteJob(ScheduleUtils.getJobKey(jobId, jobGroup));
+ }
+ return rows;
+ }
+
+ /**
+ * 批量删除调度信息
+ *
+ * @param jobIds 需要删除的任务ID
+ * @return 结果
+ */
+ @Override
+ @Transactional
+ public void deleteJobByIds(Long[] jobIds) throws SchedulerException
+ {
+ for (Long jobId : jobIds)
+ {
+ SysJob job = jobMapper.selectJobById(jobId);
+ deleteJob(job);
+ }
+ }
+
+ /**
+ * 任务调度状态修改
+ *
+ * @param job 调度信息
+ */
+ @Override
+ @Transactional
+ public int changeStatus(SysJob job) throws SchedulerException
+ {
+ int rows = 0;
+ String status = job.getStatus();
+ if (ScheduleConstants.Status.NORMAL.getValue().equals(status))
+ {
+ rows = resumeJob(job);
+ }
+ else if (ScheduleConstants.Status.PAUSE.getValue().equals(status))
+ {
+ rows = pauseJob(job);
+ }
+ return rows;
+ }
+
+ /**
+ * 立即运行任务
+ *
+ * @param job 调度信息
+ */
+ @Override
+ @Transactional
+ public void run(SysJob job) throws SchedulerException
+ {
+ Long jobId = job.getJobId();
+ String jobGroup = job.getJobGroup();
+ SysJob properties = selectJobById(job.getJobId());
+ // 参数
+ JobDataMap dataMap = new JobDataMap();
+ dataMap.put(ScheduleConstants.TASK_PROPERTIES, properties);
+ scheduler.triggerJob(ScheduleUtils.getJobKey(jobId, jobGroup), dataMap);
+ }
+
+ /**
+ * 新增任务
+ *
+ * @param job 调度信息 调度信息
+ */
+ @Override
+ @Transactional
+ public int insertJob(SysJob job) throws SchedulerException, TaskException
+ {
+ job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
+ int rows = jobMapper.insertJob(job);
+ if (rows > 0)
+ {
+ ScheduleUtils.createScheduleJob(scheduler, job);
+ }
+ return rows;
+ }
+
+ /**
+ * 更新任务的时间表达式
+ *
+ * @param job 调度信息
+ */
+ @Override
+ @Transactional
+ public int updateJob(SysJob job) throws SchedulerException, TaskException
+ {
+ SysJob properties = selectJobById(job.getJobId());
+ int rows = jobMapper.updateJob(job);
+ if (rows > 0)
+ {
+ updateSchedulerJob(job, properties.getJobGroup());
+ }
+ return rows;
+ }
+
+ /**
+ * 更新任务
+ *
+ * @param job 任务对象
+ * @param jobGroup 任务组名
+ */
+ public void updateSchedulerJob(SysJob job, String jobGroup) throws SchedulerException, TaskException
+ {
+ Long jobId = job.getJobId();
+ // 判断是否存在
+ JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup);
+ if (scheduler.checkExists(jobKey))
+ {
+ // 防止创建时存在数据问题 先移除,然后在执行创建操作
+ scheduler.deleteJob(jobKey);
+ }
+ ScheduleUtils.createScheduleJob(scheduler, job);
+ }
+
+ /**
+ * 校验cron表达式是否有效
+ *
+ * @param cronExpression 表达式
+ * @return 结果
+ */
+ @Override
+ public boolean checkCronExpressionIsValid(String cronExpression)
+ {
+ return CronUtils.isValid(cronExpression);
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/task/RyTask.java b/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/task/RyTask.java
new file mode 100644
index 0000000..01bd14a
--- /dev/null
+++ b/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/task/RyTask.java
@@ -0,0 +1,28 @@
+package com.ruoyi.job.task;
+
+import org.springframework.stereotype.Component;
+import com.ruoyi.common.core.utils.StringUtils;
+
+/**
+ * 定时任务调度测试
+ *
+ * @author ruoyi
+ */
+@Component("ryTask")
+public class RyTask
+{
+ public void ryMultipleParams(String s, Boolean b, Long l, Double d, Integer i)
+ {
+ System.out.println(StringUtils.format("执行多参方法: 字符串类型{},布尔类型{},长整型{},浮点型{},整形{}", s, b, l, d, i));
+ }
+
+ public void ryParams(String params)
+ {
+ System.out.println("执行有参方法:" + params);
+ }
+
+ public void ryNoParams()
+ {
+ System.out.println("执行无参方法");
+ }
+}
diff --git a/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/util/AbstractQuartzJob.java b/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/util/AbstractQuartzJob.java
new file mode 100644
index 0000000..d79f001
--- /dev/null
+++ b/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/util/AbstractQuartzJob.java
@@ -0,0 +1,106 @@
+package com.ruoyi.job.util;
+
+import java.util.Date;
+import org.quartz.Job;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import com.ruoyi.common.core.constant.ScheduleConstants;
+import com.ruoyi.common.core.utils.ExceptionUtil;
+import com.ruoyi.common.core.utils.SpringUtils;
+import com.ruoyi.common.core.utils.StringUtils;
+import com.ruoyi.common.core.utils.bean.BeanUtils;
+import com.ruoyi.job.domain.SysJob;
+import com.ruoyi.job.domain.SysJobLog;
+import com.ruoyi.job.service.ISysJobLogService;
+
+/**
+ * 抽象quartz调用
+ *
+ * @author ruoyi
+ */
+public abstract class AbstractQuartzJob implements Job
+{
+ private static final Logger log = LoggerFactory.getLogger(AbstractQuartzJob.class);
+
+ /**
+ * 线程本地变量
+ */
+ private static ThreadLocal<Date> threadLocal = new ThreadLocal<>();
+
+ @Override
+ public void execute(JobExecutionContext context) throws JobExecutionException
+ {
+ SysJob sysJob = new SysJob();
+ BeanUtils.copyBeanProp(sysJob, context.getMergedJobDataMap().get(ScheduleConstants.TASK_PROPERTIES));
+ try
+ {
+ before(context, sysJob);
+ if (sysJob != null)
+ {
+ doExecute(context, sysJob);
+ }
+ after(context, sysJob, null);
+ }
+ catch (Exception e)
+ {
+ log.error("任务执行异常 - :", e);
+ after(context, sysJob, e);
+ }
+ }
+
+ /**
+ * 执行前
+ *
+ * @param context 工作执行上下文对象
+ * @param sysJob 系统计划任务
+ */
+ protected void before(JobExecutionContext context, SysJob sysJob)
+ {
+ threadLocal.set(new Date());
+ }
+
+ /**
+ * 执行后
+ *
+ * @param context 工作执行上下文对象
+ * @param sysScheduleJob 系统计划任务
+ */
+ protected void after(JobExecutionContext context, SysJob sysJob, Exception e)
+ {
+ Date startTime = threadLocal.get();
+ threadLocal.remove();
+
+ final SysJobLog sysJobLog = new SysJobLog();
+ sysJobLog.setJobName(sysJob.getJobName());
+ sysJobLog.setJobGroup(sysJob.getJobGroup());
+ sysJobLog.setInvokeTarget(sysJob.getInvokeTarget());
+ sysJobLog.setStartTime(startTime);
+ sysJobLog.setStopTime(new Date());
+ long runMs = sysJobLog.getStopTime().getTime() - sysJobLog.getStartTime().getTime();
+ sysJobLog.setJobMessage(sysJobLog.getJobName() + " 总共耗时:" + runMs + "毫秒");
+ if (e != null)
+ {
+ sysJobLog.setStatus("1");
+ String errorMsg = StringUtils.substring(ExceptionUtil.getExceptionMessage(e), 0, 2000);
+ sysJobLog.setExceptionInfo(errorMsg);
+ }
+ else
+ {
+ sysJobLog.setStatus("0");
+ }
+
+ // 写入数据库当中
+ SpringUtils.getBean(ISysJobLogService.class).addJobLog(sysJobLog);
+ }
+
+ /**
+ * 执行方法,由子类重载
+ *
+ * @param context 工作执行上下文对象
+ * @param sysJob 系统计划任务
+ * @throws Exception 执行过程中的异常
+ */
+ protected abstract void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception;
+}
diff --git a/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/util/CronUtils.java b/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/util/CronUtils.java
new file mode 100644
index 0000000..98a4147
--- /dev/null
+++ b/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/util/CronUtils.java
@@ -0,0 +1,63 @@
+package com.ruoyi.job.util;
+
+import java.text.ParseException;
+import java.util.Date;
+import org.quartz.CronExpression;
+
+/**
+ * cron表达式工具类
+ *
+ * @author ruoyi
+ *
+ */
+public class CronUtils
+{
+ /**
+ * 返回一个布尔值代表一个给定的Cron表达式的有效性
+ *
+ * @param cronExpression Cron表达式
+ * @return boolean 表达式是否有效
+ */
+ public static boolean isValid(String cronExpression)
+ {
+ return CronExpression.isValidExpression(cronExpression);
+ }
+
+ /**
+ * 返回一个字符串值,表示该消息无效Cron表达式给出有效性
+ *
+ * @param cronExpression Cron表达式
+ * @return String 无效时返回表达式错误描述,如果有效返回null
+ */
+ public static String getInvalidMessage(String cronExpression)
+ {
+ try
+ {
+ new CronExpression(cronExpression);
+ return null;
+ }
+ catch (ParseException pe)
+ {
+ return pe.getMessage();
+ }
+ }
+
+ /**
+ * 返回下一个执行时间根据给定的Cron表达式
+ *
+ * @param cronExpression Cron表达式
+ * @return Date 下次Cron表达式执行时间
+ */
+ public static Date getNextExecution(String cronExpression)
+ {
+ try
+ {
+ CronExpression cron = new CronExpression(cronExpression);
+ return cron.getNextValidTimeAfter(new Date(System.currentTimeMillis()));
+ }
+ catch (ParseException e)
+ {
+ throw new IllegalArgumentException(e.getMessage());
+ }
+ }
+}
diff --git a/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/util/JobInvokeUtil.java b/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/util/JobInvokeUtil.java
new file mode 100644
index 0000000..844556f
--- /dev/null
+++ b/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/util/JobInvokeUtil.java
@@ -0,0 +1,182 @@
+package com.ruoyi.job.util;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.LinkedList;
+import java.util.List;
+import com.ruoyi.common.core.utils.SpringUtils;
+import com.ruoyi.common.core.utils.StringUtils;
+import com.ruoyi.job.domain.SysJob;
+
+/**
+ * 任务执行工具
+ *
+ * @author ruoyi
+ */
+public class JobInvokeUtil
+{
+ /**
+ * 执行方法
+ *
+ * @param sysJob 系统任务
+ */
+ public static void invokeMethod(SysJob sysJob) throws Exception
+ {
+ String invokeTarget = sysJob.getInvokeTarget();
+ String beanName = getBeanName(invokeTarget);
+ String methodName = getMethodName(invokeTarget);
+ List<Object[]> methodParams = getMethodParams(invokeTarget);
+
+ if (!isValidClassName(beanName))
+ {
+ Object bean = SpringUtils.getBean(beanName);
+ invokeMethod(bean, methodName, methodParams);
+ }
+ else
+ {
+ Object bean = Class.forName(beanName).newInstance();
+ invokeMethod(bean, methodName, methodParams);
+ }
+ }
+
+ /**
+ * 调用任务方法
+ *
+ * @param bean 目标对象
+ * @param methodName 方法名称
+ * @param methodParams 方法参数
+ */
+ private static void invokeMethod(Object bean, String methodName, List<Object[]> methodParams)
+ throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException,
+ InvocationTargetException
+ {
+ if (StringUtils.isNotNull(methodParams) && methodParams.size() > 0)
+ {
+ Method method = bean.getClass().getDeclaredMethod(methodName, getMethodParamsType(methodParams));
+ method.invoke(bean, getMethodParamsValue(methodParams));
+ }
+ else
+ {
+ Method method = bean.getClass().getDeclaredMethod(methodName);
+ method.invoke(bean);
+ }
+ }
+
+ /**
+ * 校验是否为为class包名
+ *
+ * @param str 名称
+ * @return true是 false否
+ */
+ public static boolean isValidClassName(String invokeTarget)
+ {
+ return StringUtils.countMatches(invokeTarget, ".") > 1;
+ }
+
+ /**
+ * 获取bean名称
+ *
+ * @param invokeTarget 目标字符串
+ * @return bean名称
+ */
+ public static String getBeanName(String invokeTarget)
+ {
+ String beanName = StringUtils.substringBefore(invokeTarget, "(");
+ return StringUtils.substringBeforeLast(beanName, ".");
+ }
+
+ /**
+ * 获取bean方法
+ *
+ * @param invokeTarget 目标字符串
+ * @return method方法
+ */
+ public static String getMethodName(String invokeTarget)
+ {
+ String methodName = StringUtils.substringBefore(invokeTarget, "(");
+ return StringUtils.substringAfterLast(methodName, ".");
+ }
+
+ /**
+ * 获取method方法参数相关列表
+ *
+ * @param invokeTarget 目标字符串
+ * @return method方法相关参数列表
+ */
+ public static List<Object[]> getMethodParams(String invokeTarget)
+ {
+ String methodStr = StringUtils.substringBetween(invokeTarget, "(", ")");
+ if (StringUtils.isEmpty(methodStr))
+ {
+ return null;
+ }
+ String[] methodParams = methodStr.split(",");
+ List<Object[]> classs = new LinkedList<>();
+ for (int i = 0; i < methodParams.length; i++)
+ {
+ String str = StringUtils.trimToEmpty(methodParams[i]);
+ // String字符串类型,包含'
+ if (StringUtils.contains(str, "'"))
+ {
+ classs.add(new Object[] { StringUtils.replace(str, "'", ""), String.class });
+ }
+ // boolean布尔类型,等于true或者false
+ else if (StringUtils.equals(str, "true") || StringUtils.equalsIgnoreCase(str, "false"))
+ {
+ classs.add(new Object[] { Boolean.valueOf(str), Boolean.class });
+ }
+ // long长整形,包含L
+ else if (StringUtils.containsIgnoreCase(str, "L"))
+ {
+ classs.add(new Object[] { Long.valueOf(StringUtils.replaceIgnoreCase(str, "L", "")), Long.class });
+ }
+ // double浮点类型,包含D
+ else if (StringUtils.containsIgnoreCase(str, "D"))
+ {
+ classs.add(new Object[] { Double.valueOf(StringUtils.replaceIgnoreCase(str, "D", "")), Double.class });
+ }
+ // 其他类型归类为整形
+ else
+ {
+ classs.add(new Object[] { Integer.valueOf(str), Integer.class });
+ }
+ }
+ return classs;
+ }
+
+ /**
+ * 获取参数类型
+ *
+ * @param methodParams 参数相关列表
+ * @return 参数类型列表
+ */
+ public static Class<?>[] getMethodParamsType(List<Object[]> methodParams)
+ {
+ Class<?>[] classs = new Class<?>[methodParams.size()];
+ int index = 0;
+ for (Object[] os : methodParams)
+ {
+ classs[index] = (Class<?>) os[1];
+ index++;
+ }
+ return classs;
+ }
+
+ /**
+ * 获取参数值
+ *
+ * @param methodParams 参数相关列表
+ * @return 参数值列表
+ */
+ public static Object[] getMethodParamsValue(List<Object[]> methodParams)
+ {
+ Object[] classs = new Object[methodParams.size()];
+ int index = 0;
+ for (Object[] os : methodParams)
+ {
+ classs[index] = (Object) os[0];
+ index++;
+ }
+ return classs;
+ }
+}
diff --git a/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/util/QuartzDisallowConcurrentExecution.java b/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/util/QuartzDisallowConcurrentExecution.java
new file mode 100644
index 0000000..88e27d3
--- /dev/null
+++ b/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/util/QuartzDisallowConcurrentExecution.java
@@ -0,0 +1,22 @@
+package com.ruoyi.job.util;
+
+import org.quartz.DisallowConcurrentExecution;
+import org.quartz.JobExecutionContext;
+
+import com.ruoyi.job.domain.SysJob;
+
+/**
+ * 定时任务处理(禁止并发执行)
+ *
+ * @author ruoyi
+ *
+ */
+@DisallowConcurrentExecution
+public class QuartzDisallowConcurrentExecution extends AbstractQuartzJob
+{
+ @Override
+ protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception
+ {
+ JobInvokeUtil.invokeMethod(sysJob);
+ }
+}
diff --git a/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/util/QuartzJobExecution.java b/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/util/QuartzJobExecution.java
new file mode 100644
index 0000000..246b693
--- /dev/null
+++ b/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/util/QuartzJobExecution.java
@@ -0,0 +1,20 @@
+package com.ruoyi.job.util;
+
+import org.quartz.JobExecutionContext;
+
+import com.ruoyi.job.domain.SysJob;
+
+/**
+ * 定时任务处理(允许并发执行)
+ *
+ * @author ruoyi
+ *
+ */
+public class QuartzJobExecution extends AbstractQuartzJob
+{
+ @Override
+ protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception
+ {
+ JobInvokeUtil.invokeMethod(sysJob);
+ }
+}
diff --git a/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/util/ScheduleUtils.java b/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/util/ScheduleUtils.java
new file mode 100644
index 0000000..7e7657f
--- /dev/null
+++ b/ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/util/ScheduleUtils.java
@@ -0,0 +1,113 @@
+package com.ruoyi.job.util;
+
+import org.quartz.CronScheduleBuilder;
+import org.quartz.CronTrigger;
+import org.quartz.Job;
+import org.quartz.JobBuilder;
+import org.quartz.JobDetail;
+import org.quartz.JobKey;
+import org.quartz.Scheduler;
+import org.quartz.SchedulerException;
+import org.quartz.TriggerBuilder;
+import org.quartz.TriggerKey;
+import com.ruoyi.common.core.constant.ScheduleConstants;
+import com.ruoyi.common.core.exception.job.TaskException;
+import com.ruoyi.common.core.exception.job.TaskException.Code;
+import com.ruoyi.job.domain.SysJob;
+
+/**
+ * 定时任务工具类
+ *
+ * @author ruoyi
+ *
+ */
+public class ScheduleUtils
+{
+ /**
+ * 得到quartz任务类
+ *
+ * @param sysJob 执行计划
+ * @return 具体执行任务类
+ */
+ private static Class<? extends Job> getQuartzJobClass(SysJob sysJob)
+ {
+ boolean isConcurrent = "0".equals(sysJob.getConcurrent());
+ return isConcurrent ? QuartzJobExecution.class : QuartzDisallowConcurrentExecution.class;
+ }
+
+ /**
+ * 构建任务触发对象
+ */
+ public static TriggerKey getTriggerKey(Long jobId, String jobGroup)
+ {
+ return TriggerKey.triggerKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup);
+ }
+
+ /**
+ * 构建任务键对象
+ */
+ public static JobKey getJobKey(Long jobId, String jobGroup)
+ {
+ return JobKey.jobKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup);
+ }
+
+ /**
+ * 创建定时任务
+ */
+ public static void createScheduleJob(Scheduler scheduler, SysJob job) throws SchedulerException, TaskException
+ {
+ Class<? extends Job> jobClass = getQuartzJobClass(job);
+ // 构建job信息
+ Long jobId = job.getJobId();
+ String jobGroup = job.getJobGroup();
+ JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(getJobKey(jobId, jobGroup)).build();
+
+ // 表达式调度构建器
+ CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
+ cronScheduleBuilder = handleCronScheduleMisfirePolicy(job, cronScheduleBuilder);
+
+ // 按新的cronExpression表达式构建一个新的trigger
+ CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(jobId, jobGroup))
+ .withSchedule(cronScheduleBuilder).build();
+
+ // 放入参数,运行时的方法可以获取
+ jobDetail.getJobDataMap().put(ScheduleConstants.TASK_PROPERTIES, job);
+
+ // 判断是否存在
+ if (scheduler.checkExists(getJobKey(jobId, jobGroup)))
+ {
+ // 防止创建时存在数据问题 先移除,然后在执行创建操作
+ scheduler.deleteJob(getJobKey(jobId, jobGroup));
+ }
+
+ scheduler.scheduleJob(jobDetail, trigger);
+
+ // 暂停任务
+ if (job.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue()))
+ {
+ scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup));
+ }
+ }
+
+ /**
+ * 设置定时任务策略
+ */
+ public static CronScheduleBuilder handleCronScheduleMisfirePolicy(SysJob job, CronScheduleBuilder cb)
+ throws TaskException
+ {
+ switch (job.getMisfirePolicy())
+ {
+ case ScheduleConstants.MISFIRE_DEFAULT:
+ return cb;
+ case ScheduleConstants.MISFIRE_IGNORE_MISFIRES:
+ return cb.withMisfireHandlingInstructionIgnoreMisfires();
+ case ScheduleConstants.MISFIRE_FIRE_AND_PROCEED:
+ return cb.withMisfireHandlingInstructionFireAndProceed();
+ case ScheduleConstants.MISFIRE_DO_NOTHING:
+ return cb.withMisfireHandlingInstructionDoNothing();
+ default:
+ throw new TaskException("The task misfire policy '" + job.getMisfirePolicy()
+ + "' cannot be used in cron schedule tasks", Code.CONFIG_ERROR);
+ }
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-job/src/main/resources/banner.txt b/ruoyi-modules/ruoyi-job/src/main/resources/banner.txt
new file mode 100644
index 0000000..252407e
--- /dev/null
+++ b/ruoyi-modules/ruoyi-job/src/main/resources/banner.txt
@@ -0,0 +1,10 @@
+Spring Boot Version: ${spring-boot.version}
+Spring Application Name: ${spring.application.name}
+ _ _ _
+ (_) (_) | |
+ _ __ _ _ ___ _ _ _ ______ _ ___ | |__
+| '__|| | | | / _ \ | | | || ||______| | | / _ \ | '_ \
+| | | |_| || (_) || |_| || | | || (_) || |_) |
+|_| \__,_| \___/ \__, ||_| | | \___/ |_.__/
+ __/ | _/ |
+ |___/ |__/
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-job/src/main/resources/bootstrap.yml b/ruoyi-modules/ruoyi-job/src/main/resources/bootstrap.yml
new file mode 100644
index 0000000..480a180
--- /dev/null
+++ b/ruoyi-modules/ruoyi-job/src/main/resources/bootstrap.yml
@@ -0,0 +1,24 @@
+# Tomcat
+server:
+ port: 9203
+
+# Spring
+spring:
+ application:
+ # 应用名称
+ name: ruoyi-job
+ profiles:
+ # 环境配置
+ active: dev
+ cloud:
+ nacos:
+ discovery:
+ # 服务注册地址
+ server-addr: 127.0.0.1:8848
+ config:
+ # 配置中心地址
+ server-addr: 127.0.0.1:8848
+ # 配置文件格式
+ file-extension: yml
+ # 共享配置
+ shared-dataids: application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
diff --git a/ruoyi-modules/ruoyi-job/src/main/resources/logback.xml b/ruoyi-modules/ruoyi-job/src/main/resources/logback.xml
new file mode 100644
index 0000000..74a6364
--- /dev/null
+++ b/ruoyi-modules/ruoyi-job/src/main/resources/logback.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration scan="true" scanPeriod="60 seconds" debug="false">
+ <!-- 日志存放路径 -->
+ <property name="log.path" value="/home/ruoyi/logs/ruoyi-job" />
+ <!-- 日志输出格式 -->
+ <property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />:ss} %-5level ${springAppName:-} %thread %logger %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}/info.log</file>
+ <!-- 循环政策:基于时间创建日志文件 -->
+ <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+ <!-- 日志文件名格式 -->
+ <fileNamePattern>${log.path}/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}/error.log</file>
+ <!-- 循环政策:基于时间创建日志文件 -->
+ <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+ <!-- 日志文件名格式 -->
+ <fileNamePattern>${log.path}/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>
+
+ <!-- 系统模块日志级别控制 -->
+ <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>
+</configuration>
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-job/src/main/resources/mapper/job/SysJobLogMapper.xml b/ruoyi-modules/ruoyi-job/src/main/resources/mapper/job/SysJobLogMapper.xml
new file mode 100644
index 0000000..8b85a22
--- /dev/null
+++ b/ruoyi-modules/ruoyi-job/src/main/resources/mapper/job/SysJobLogMapper.xml
@@ -0,0 +1,93 @@
+<?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.job.mapper.SysJobLogMapper">
+
+ <resultMap type="SysJobLog" id="SysJobLogResult">
+ <id property="jobLogId" column="job_log_id" />
+ <result property="jobName" column="job_name" />
+ <result property="jobGroup" column="job_group" />
+ <result property="invokeTarget" column="invoke_target" />
+ <result property="jobMessage" column="job_message" />
+ <result property="status" column="status" />
+ <result property="exceptionInfo" column="exception_info" />
+ <result property="createTime" column="create_time" />
+ </resultMap>
+
+ <sql id="selectJobLogVo">
+ select job_log_id, job_name, job_group, invoke_target, job_message, status, exception_info, create_time
+ from sys_job_log
+ </sql>
+
+ <select id="selectJobLogList" parameterType="SysJobLog" resultMap="SysJobLogResult">
+ <include refid="selectJobLogVo"/>
+ <where>
+ <if test="jobName != null and jobName != ''">
+ AND job_name like concat('%', #{jobName}, '%')
+ </if>
+ <if test="jobGroup != null and jobGroup != ''">
+ AND job_group = #{jobGroup}
+ </if>
+ <if test="status != null and status != ''">
+ AND status = #{status}
+ </if>
+ <if test="invokeTarget != null and invokeTarget != ''">
+ AND invoke_target like concat('%', #{invokeTarget}, '%')
+ </if>
+ <if test="beginTime != null and beginTime != ''"><!-- 开始时间检索 -->
+ and date_format(create_time,'%y%m%d') >= date_format(#{beginTime},'%y%m%d')
+ </if>
+ <if test="endTime != null and endTime != ''"><!-- 结束时间检索 -->
+ and date_format(create_time,'%y%m%d') <= date_format(#{endTime},'%y%m%d')
+ </if>
+ </where>
+ </select>
+
+ <select id="selectJobLogAll" resultMap="SysJobLogResult">
+ <include refid="selectJobLogVo"/>
+ </select>
+
+ <select id="selectJobLogById" parameterType="Long" resultMap="SysJobLogResult">
+ <include refid="selectJobLogVo"/>
+ where job_log_id = #{jobLogId}
+ </select>
+
+ <delete id="deleteJobLogById" parameterType="Long">
+ delete from sys_job_log where job_log_id = #{jobLogId}
+ </delete>
+
+ <delete id="deleteJobLogByIds" parameterType="Long">
+ delete from sys_job_log where job_log_id in
+ <foreach collection="array" item="jobLogId" open="(" separator="," close=")">
+ #{jobLogId}
+ </foreach>
+ </delete>
+
+ <update id="cleanJobLog">
+ truncate table sys_job_log
+ </update>
+
+ <insert id="insertJobLog" parameterType="SysJobLog">
+ insert into sys_job_log(
+ <if test="jobLogId != null and jobLogId != 0">job_log_id,</if>
+ <if test="jobName != null and jobName != ''">job_name,</if>
+ <if test="jobGroup != null and jobGroup != ''">job_group,</if>
+ <if test="invokeTarget != null and invokeTarget != ''">invoke_target,</if>
+ <if test="jobMessage != null and jobMessage != ''">job_message,</if>
+ <if test="status != null and status != ''">status,</if>
+ <if test="exceptionInfo != null and exceptionInfo != ''">exception_info,</if>
+ create_time
+ )values(
+ <if test="jobLogId != null and jobLogId != 0">#{jobLogId},</if>
+ <if test="jobName != null and jobName != ''">#{jobName},</if>
+ <if test="jobGroup != null and jobGroup != ''">#{jobGroup},</if>
+ <if test="invokeTarget != null and invokeTarget != ''">#{invokeTarget},</if>
+ <if test="jobMessage != null and jobMessage != ''">#{jobMessage},</if>
+ <if test="status != null and status != ''">#{status},</if>
+ <if test="exceptionInfo != null and exceptionInfo != ''">#{exceptionInfo},</if>
+ sysdate()
+ )
+ </insert>
+
+</mapper>
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-job/src/main/resources/mapper/job/SysJobMapper.xml b/ruoyi-modules/ruoyi-job/src/main/resources/mapper/job/SysJobMapper.xml
new file mode 100644
index 0000000..0323cf1
--- /dev/null
+++ b/ruoyi-modules/ruoyi-job/src/main/resources/mapper/job/SysJobMapper.xml
@@ -0,0 +1,111 @@
+<?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.job.mapper.SysJobMapper">
+
+ <resultMap type="SysJob" id="SysJobResult">
+ <id property="jobId" column="job_id" />
+ <result property="jobName" column="job_name" />
+ <result property="jobGroup" column="job_group" />
+ <result property="invokeTarget" column="invoke_target" />
+ <result property="cronExpression" column="cron_expression" />
+ <result property="misfirePolicy" column="misfire_policy" />
+ <result property="concurrent" column="concurrent" />
+ <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="selectJobVo">
+ select job_id, job_name, job_group, invoke_target, cron_expression, misfire_policy, concurrent, status, create_by, create_time, remark
+ from sys_job
+ </sql>
+
+ <select id="selectJobList" parameterType="SysJob" resultMap="SysJobResult">
+ <include refid="selectJobVo"/>
+ <where>
+ <if test="jobName != null and jobName != ''">
+ AND job_name like concat('%', #{jobName}, '%')
+ </if>
+ <if test="jobGroup != null and jobGroup != ''">
+ AND job_group = #{jobGroup}
+ </if>
+ <if test="status != null and status != ''">
+ AND status = #{status}
+ </if>
+ <if test="invokeTarget != null and invokeTarget != ''">
+ AND invoke_target like concat('%', #{invokeTarget}, '%')
+ </if>
+ </where>
+ </select>
+
+ <select id="selectJobAll" resultMap="SysJobResult">
+ <include refid="selectJobVo"/>
+ </select>
+
+ <select id="selectJobById" parameterType="Long" resultMap="SysJobResult">
+ <include refid="selectJobVo"/>
+ where job_id = #{jobId}
+ </select>
+
+ <delete id="deleteJobById" parameterType="Long">
+ delete from sys_job where job_id = #{jobId}
+ </delete>
+
+ <delete id="deleteJobByIds" parameterType="Long">
+ delete from sys_job where job_id in
+ <foreach collection="array" item="jobId" open="(" separator="," close=")">
+ #{jobId}
+ </foreach>
+ </delete>
+
+ <update id="updateJob" parameterType="SysJob">
+ update sys_job
+ <set>
+ <if test="jobName != null and jobName != ''">job_name = #{jobName},</if>
+ <if test="jobGroup != null and jobGroup != ''">job_group = #{jobGroup},</if>
+ <if test="invokeTarget != null and invokeTarget != ''">invoke_target = #{invokeTarget},</if>
+ <if test="cronExpression != null and cronExpression != ''">cron_expression = #{cronExpression},</if>
+ <if test="misfirePolicy != null and misfirePolicy != ''">misfire_policy = #{misfirePolicy},</if>
+ <if test="concurrent != null and concurrent != ''">concurrent = #{concurrent},</if>
+ <if test="status !=null">status = #{status},</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 job_id = #{jobId}
+ </update>
+
+ <insert id="insertJob" parameterType="SysJob" useGeneratedKeys="true" keyProperty="jobId">
+ insert into sys_job(
+ <if test="jobId != null and jobId != 0">job_id,</if>
+ <if test="jobName != null and jobName != ''">job_name,</if>
+ <if test="jobGroup != null and jobGroup != ''">job_group,</if>
+ <if test="invokeTarget != null and invokeTarget != ''">invoke_target,</if>
+ <if test="cronExpression != null and cronExpression != ''">cron_expression,</if>
+ <if test="misfirePolicy != null and misfirePolicy != ''">misfire_policy,</if>
+ <if test="concurrent != null and concurrent != ''">concurrent,</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="jobId != null and jobId != 0">#{jobId},</if>
+ <if test="jobName != null and jobName != ''">#{jobName},</if>
+ <if test="jobGroup != null and jobGroup != ''">#{jobGroup},</if>
+ <if test="invokeTarget != null and invokeTarget != ''">#{invokeTarget},</if>
+ <if test="cronExpression != null and cronExpression != ''">#{cronExpression},</if>
+ <if test="misfirePolicy != null and misfirePolicy != ''">#{misfirePolicy},</if>
+ <if test="concurrent != null and concurrent != ''">#{concurrent},</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>
+
+</mapper>
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-system/pom.xml b/ruoyi-modules/ruoyi-system/pom.xml
new file mode 100644
index 0000000..bfb644b
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/pom.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns="http://maven.apache.org/POM/4.0.0"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-modules</artifactId>
+ <version>1.0.0</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>ruoyi-modules-system</artifactId>
+
+ <description>
+ ruoyi-modules-system系统模块
+ </description>
+
+ <dependencies>
+
+ <!-- SpringCloud Ailibaba Nacos -->
+ <dependency>
+ <groupId>com.alibaba.cloud</groupId>
+ <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
+ </dependency>
+
+ <!-- SpringCloud Ailibaba Nacos Config -->
+ <dependency>
+ <groupId>com.alibaba.cloud</groupId>
+ <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
+ </dependency>
+
+ <!-- SpringCloud Netflix Hystrix -->
+ <dependency>
+ <groupId>org.springframework.cloud</groupId>
+ <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
+ </dependency>
+
+ <!-- SpringBoot Web -->
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-web</artifactId>
+ </dependency>
+
+ <!-- Swagger -->
+ <dependency>
+ <groupId>io.springfox</groupId>
+ <artifactId>springfox-swagger-ui</artifactId>
+ <version>${swagger.fox.version}</version>
+ </dependency>
+
+ <!-- Mysql Connector -->
+ <dependency>
+ <groupId>mysql</groupId>
+ <artifactId>mysql-connector-java</artifactId>
+ </dependency>
+
+ <!-- Ruoyi Common Security -->
+ <dependency>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-common-security</artifactId>
+ </dependency>
+
+ <!-- Ruoyi Common Datascope -->
+ <dependency>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-common-datascope</artifactId>
+ </dependency>
+
+ <!-- Ruoyi Common Log -->
+ <dependency>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-common-log</artifactId>
+ </dependency>
+
+ <!-- Ruoyi Common Swagger -->
+ <dependency>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-common-swagger</artifactId>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>repackage</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/RuoYiSystemApplication.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/RuoYiSystemApplication.java
new file mode 100644
index 0000000..ee24c93
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/RuoYiSystemApplication.java
@@ -0,0 +1,34 @@
+package com.ruoyi.system;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.cloud.client.SpringCloudApplication;
+import com.ruoyi.common.security.annotation.EnableCustomConfig;
+import com.ruoyi.common.security.annotation.EnableRyFeignClients;
+import com.ruoyi.common.swagger.annotation.EnableCustomSwagger2;
+
+/**
+ * 系统模块
+ *
+ * @author ruoyi
+ */
+@EnableCustomConfig
+@EnableCustomSwagger2
+@EnableRyFeignClients
+@SpringCloudApplication
+public class RuoYiSystemApplication
+{
+ public static void main(String[] args)
+ {
+ SpringApplication.run(RuoYiSystemApplication.class, args);
+ System.out.println("(♥◠‿◠)ノ゙ 系统模块启动成功 ლ(´ڡ`ლ)゙ \n" +
+ " .-------. ____ __ \n" +
+ " | _ _ \\ \\ \\ / / \n" +
+ " | ( ' ) | \\ _. / ' \n" +
+ " |(_ o _) / _( )_ .' \n" +
+ " | (_,_).' __ ___(_ o _)' \n" +
+ " | |\\ \\ | || |(_,_)' \n" +
+ " | | \\ `' /| `-' / \n" +
+ " | | \\ / \\ / \n" +
+ " ''-' `'-' `-..-' ");
+ }
+}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysConfigController.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysConfigController.java
new file mode 100644
index 0000000..37c633d
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysConfigController.java
@@ -0,0 +1,123 @@
+package com.ruoyi.system.controller;
+
+import java.io.IOException;
+import java.util.List;
+import javax.servlet.http.HttpServletResponse;
+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.RestController;
+import com.ruoyi.common.core.constant.UserConstants;
+import com.ruoyi.common.core.utils.poi.ExcelUtil;
+import com.ruoyi.common.core.web.controller.BaseController;
+import com.ruoyi.common.core.web.domain.AjaxResult;
+import com.ruoyi.common.core.web.page.TableDataInfo;
+import com.ruoyi.common.log.annotation.Log;
+import com.ruoyi.common.log.enums.BusinessType;
+import com.ruoyi.common.security.utils.SecurityUtils;
+import com.ruoyi.system.domain.SysConfig;
+import com.ruoyi.system.service.ISysConfigService;
+
+/**
+ * 参数配置 信息操作处理
+ *
+ * @author ruoyi
+ */
+@RestController
+@RequestMapping("/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);
+ }
+
+ @Log(title = "参数管理", businessType = BusinessType.EXPORT)
+ @PreAuthorize("@ss.hasPermi('system:config:export')")
+ @PostMapping("/export")
+ public void export(HttpServletResponse response, SysConfig config) throws IOException
+ {
+ List<SysConfig> list = configService.selectConfigList(config);
+ ExcelUtil<SysConfig> util = new ExcelUtil<SysConfig>(SysConfig.class);
+ util.exportExcel(response, list, "参数数据");
+ }
+
+ /**
+ * 根据参数编号获取详细信息
+ */
+ @PreAuthorize("@ss.hasRole('admin')")
+ @GetMapping(value = "/{configId}")
+ public AjaxResult getInfo(@PathVariable Long configId)
+ {
+ return AjaxResult.success(configService.selectConfigById(configId));
+ }
+
+ /**
+ * 根据参数键名查询参数值
+ */
+ @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(@Validated @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(@Validated @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("/{configIds}")
+ public AjaxResult remove(@PathVariable Long[] configIds)
+ {
+ return toAjax(configService.deleteConfigByIds(configIds));
+ }
+}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysDeptController.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysDeptController.java
new file mode 100644
index 0000000..d6a49d4
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysDeptController.java
@@ -0,0 +1,165 @@
+package com.ruoyi.system.controller;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.commons.lang3.ArrayUtils;
+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.RestController;
+
+import com.ruoyi.common.core.constant.UserConstants;
+import com.ruoyi.common.core.utils.StringUtils;
+import com.ruoyi.common.core.web.controller.BaseController;
+import com.ruoyi.common.core.web.domain.AjaxResult;
+import com.ruoyi.common.log.annotation.Log;
+import com.ruoyi.common.log.enums.BusinessType;
+import com.ruoyi.common.security.utils.SecurityUtils;
+import com.ruoyi.system.api.domain.SysDept;
+import com.ruoyi.system.service.ISysDeptService;
+
+/**
+ * 部门信息
+ *
+ * @author ruoyi
+ */
+@RestController
+@RequestMapping("/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(depts);
+ }
+
+ /**
+ * 查询部门列表(排除节点)
+ */
+ @PreAuthorize("@ss.hasPermi('system:dept:list')")
+ @GetMapping("/list/exclude/{deptId}")
+ public AjaxResult excludeChild(@PathVariable(value = "deptId", required = false) Long deptId)
+ {
+ List<SysDept> depts = deptService.selectDeptList(new SysDept());
+ Iterator<SysDept> it = depts.iterator();
+ while (it.hasNext())
+ {
+ SysDept d = (SysDept) it.next();
+ if (d.getDeptId().intValue() == deptId
+ || ArrayUtils.contains(StringUtils.split(d.getAncestors(), ","), deptId + ""))
+ {
+ it.remove();
+ }
+ }
+ return AjaxResult.success(depts);
+ }
+
+ /**
+ * 根据部门编号获取详细信息
+ */
+ @PreAuthorize("@ss.hasPermi('system:dept:query')")
+ @GetMapping(value = "/{deptId}")
+ public AjaxResult getInfo(@PathVariable Long deptId)
+ {
+ return AjaxResult.success(deptService.selectDeptById(deptId));
+ }
+
+ /**
+ * 获取部门下拉树列表
+ */
+ @GetMapping("/treeselect")
+ public AjaxResult treeselect(SysDept dept)
+ {
+ List<SysDept> depts = deptService.selectDeptList(dept);
+ return AjaxResult.success(deptService.buildDeptTreeSelect(depts));
+ }
+
+ /**
+ * 加载对应角色部门列表树
+ */
+ @GetMapping(value = "/roleDeptTreeselect/{roleId}")
+ public AjaxResult roleDeptTreeselect(@PathVariable("roleId") Long roleId)
+ {
+ List<SysDept> depts = deptService.selectDeptList(new SysDept());
+ AjaxResult ajax = AjaxResult.success();
+ ajax.put("checkedKeys", deptService.selectDeptListByRoleId(roleId));
+ ajax.put("depts", deptService.buildDeptTreeSelect(depts));
+ return ajax;
+ }
+
+ /**
+ * 新增部门
+ */
+ @PreAuthorize("@ss.hasPermi('system:dept:add')")
+ @Log(title = "部门管理", businessType = BusinessType.INSERT)
+ @PostMapping
+ public AjaxResult add(@Validated @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(@Validated @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() + "'失败,上级部门不能是自己");
+ }
+ else if (StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus())
+ && deptService.selectNormalChildrenDeptById(dept.getDeptId()) > 0)
+ {
+ return AjaxResult.error("该部门包含未停用的子部门!");
+ }
+ 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-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysDictDataController.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysDictDataController.java
new file mode 100644
index 0000000..4550dbf
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysDictDataController.java
@@ -0,0 +1,111 @@
+package com.ruoyi.system.controller;
+
+import java.io.IOException;
+import java.util.List;
+import javax.servlet.http.HttpServletResponse;
+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.RestController;
+import com.ruoyi.common.core.utils.poi.ExcelUtil;
+import com.ruoyi.common.core.web.controller.BaseController;
+import com.ruoyi.common.core.web.domain.AjaxResult;
+import com.ruoyi.common.core.web.page.TableDataInfo;
+import com.ruoyi.common.log.annotation.Log;
+import com.ruoyi.common.log.enums.BusinessType;
+import com.ruoyi.common.security.utils.SecurityUtils;
+import com.ruoyi.system.domain.SysDictData;
+import com.ruoyi.system.service.ISysDictDataService;
+
+/**
+ * 数据字典信息
+ *
+ * @author ruoyi
+ */
+@RestController
+@RequestMapping("/dict/data")
+public class SysDictDataController extends BaseController
+{
+ @Autowired
+ private ISysDictDataService dictDataService;
+
+ @PreAuthorize("@ss.hasPermi('system:dict:list')")
+ @GetMapping("/list")
+ public TableDataInfo list(SysDictData dictData)
+ {
+ startPage();
+ List<SysDictData> list = dictDataService.selectDictDataList(dictData);
+ return getDataTable(list);
+ }
+
+ @Log(title = "字典数据", businessType = BusinessType.EXPORT)
+ @PreAuthorize("@ss.hasPermi('system:dict:export')")
+ @PostMapping("/export")
+ public void export(HttpServletResponse response, SysDictData dictData) throws IOException
+ {
+ List<SysDictData> list = dictDataService.selectDictDataList(dictData);
+ ExcelUtil<SysDictData> util = new ExcelUtil<SysDictData>(SysDictData.class);
+ util.exportExcel(response, list, "字典数据");
+ }
+
+ /**
+ * 查询字典数据详细
+ */
+ @PreAuthorize("@ss.hasPermi('system:dict:query')")
+ @GetMapping(value = "/{dictCode}")
+ public AjaxResult getInfo(@PathVariable Long dictCode)
+ {
+ return AjaxResult.success(dictDataService.selectDictDataById(dictCode));
+ }
+
+ /**
+ * 根据字典类型查询字典数据信息
+ */
+ @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(@Validated @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(@Validated @RequestBody SysDictData dict)
+ {
+ dict.setUpdateBy(SecurityUtils.getUsername());
+ return toAjax(dictDataService.updateDictData(dict));
+ }
+
+ /**
+ * 删除字典类型
+ */
+ @PreAuthorize("@ss.hasPermi('system:dict:remove')")
+ @Log(title = "字典类型", businessType = BusinessType.DELETE)
+ @DeleteMapping("/{dictCodes}")
+ public AjaxResult remove(@PathVariable Long[] dictCodes)
+ {
+ return toAjax(dictDataService.deleteDictDataByIds(dictCodes));
+ }
+}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysDictTypeController.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysDictTypeController.java
new file mode 100644
index 0000000..3ee1773
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysDictTypeController.java
@@ -0,0 +1,121 @@
+package com.ruoyi.system.controller;
+
+import java.io.IOException;
+import java.util.List;
+import javax.servlet.http.HttpServletResponse;
+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.RestController;
+import com.ruoyi.common.core.constant.UserConstants;
+import com.ruoyi.common.core.utils.poi.ExcelUtil;
+import com.ruoyi.common.core.web.controller.BaseController;
+import com.ruoyi.common.core.web.domain.AjaxResult;
+import com.ruoyi.common.core.web.page.TableDataInfo;
+import com.ruoyi.common.log.annotation.Log;
+import com.ruoyi.common.log.enums.BusinessType;
+import com.ruoyi.common.security.utils.SecurityUtils;
+import com.ruoyi.system.domain.SysDictType;
+import com.ruoyi.system.service.ISysDictTypeService;
+
+/**
+ * 数据字典信息
+ *
+ * @author ruoyi
+ */
+@RestController
+@RequestMapping("/dict/type")
+public class SysDictTypeController extends BaseController
+{
+ @Autowired
+ private ISysDictTypeService dictTypeService;
+
+ @PreAuthorize("@ss.hasPermi('system:dict:list')")
+ @GetMapping("/list")
+ public TableDataInfo list(SysDictType dictType)
+ {
+ startPage();
+ List<SysDictType> list = dictTypeService.selectDictTypeList(dictType);
+ return getDataTable(list);
+ }
+
+ @Log(title = "字典类型", businessType = BusinessType.EXPORT)
+ @PreAuthorize("@ss.hasPermi('system:dict:export')")
+ @PostMapping("/export")
+ public void export(HttpServletResponse response, SysDictType dictType) throws IOException
+ {
+ List<SysDictType> list = dictTypeService.selectDictTypeList(dictType);
+ ExcelUtil<SysDictType> util = new ExcelUtil<SysDictType>(SysDictType.class);
+ util.exportExcel(response, 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(@Validated @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(@Validated @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("/{dictIds}")
+ public AjaxResult remove(@PathVariable Long[] dictIds)
+ {
+ return toAjax(dictTypeService.deleteDictTypeByIds(dictIds));
+ }
+
+ /**
+ * 获取字典选择框列表
+ */
+ @GetMapping("/optionselect")
+ public AjaxResult optionselect()
+ {
+ List<SysDictType> dictTypes = dictTypeService.selectDictTypeAll();
+ return AjaxResult.success(dictTypes);
+ }
+}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysLogininforController.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysLogininforController.java
new file mode 100644
index 0000000..f5cf843
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysLogininforController.java
@@ -0,0 +1,70 @@
+package com.ruoyi.system.controller;
+
+import java.io.IOException;
+import java.util.List;
+import javax.servlet.http.HttpServletResponse;
+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.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.ruoyi.common.core.utils.poi.ExcelUtil;
+import com.ruoyi.common.core.web.controller.BaseController;
+import com.ruoyi.common.core.web.domain.AjaxResult;
+import com.ruoyi.common.core.web.page.TableDataInfo;
+import com.ruoyi.common.log.annotation.Log;
+import com.ruoyi.common.log.enums.BusinessType;
+import com.ruoyi.system.domain.SysLogininfor;
+import com.ruoyi.system.service.ISysLogininforService;
+
+/**
+ * 系统访问记录
+ *
+ * @author ruoyi
+ */
+@RestController
+@RequestMapping("/logininfor")
+public class SysLogininforController extends BaseController
+{
+ @Autowired
+ private ISysLogininforService logininforService;
+
+ @PreAuthorize("@ss.hasPermi('system:logininfor:list')")
+ @GetMapping("/list")
+ public TableDataInfo list(SysLogininfor logininfor)
+ {
+ startPage();
+ List<SysLogininfor> list = logininforService.selectLogininforList(logininfor);
+ return getDataTable(list);
+ }
+
+ @Log(title = "登陆日志", businessType = BusinessType.EXPORT)
+ @PreAuthorize("@ss.hasPermi('system:logininfor:export')")
+ @PostMapping("/export")
+ public void export(HttpServletResponse response, SysLogininfor logininfor) throws IOException
+ {
+ List<SysLogininfor> list = logininforService.selectLogininforList(logininfor);
+ ExcelUtil<SysLogininfor> util = new ExcelUtil<SysLogininfor>(SysLogininfor.class);
+ util.exportExcel(response, list, "登陆日志");
+ }
+
+ @PreAuthorize("@ss.hasPermi('system:logininfor:remove')")
+ @Log(title = "登陆日志", businessType = BusinessType.DELETE)
+ @DeleteMapping("/{infoIds}")
+ public AjaxResult remove(@PathVariable Long[] infoIds)
+ {
+ return toAjax(logininforService.deleteLogininforByIds(infoIds));
+ }
+
+ @PreAuthorize("@ss.hasPermi('system:logininfor:remove')")
+ @Log(title = "登陆日志", businessType = BusinessType.DELETE)
+ @DeleteMapping("/clean")
+ public AjaxResult clean()
+ {
+ logininforService.cleanLogininfor();
+ return AjaxResult.success();
+ }
+}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysMenuController.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysMenuController.java
new file mode 100644
index 0000000..4bd54cf
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysMenuController.java
@@ -0,0 +1,150 @@
+package com.ruoyi.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.RestController;
+import com.ruoyi.common.core.constant.UserConstants;
+import com.ruoyi.common.core.web.controller.BaseController;
+import com.ruoyi.common.core.web.domain.AjaxResult;
+import com.ruoyi.common.log.annotation.Log;
+import com.ruoyi.common.log.enums.BusinessType;
+import com.ruoyi.common.security.domain.LoginUser;
+import com.ruoyi.common.security.utils.SecurityUtils;
+import com.ruoyi.system.domain.SysMenu;
+import com.ruoyi.system.service.ISysMenuService;
+
+/**
+ * 菜单信息
+ *
+ * @author ruoyi
+ */
+@RestController
+@RequestMapping("/menu")
+public class SysMenuController extends BaseController
+{
+ @Autowired
+ private ISysMenuService menuService;
+
+ /**
+ * 获取菜单列表
+ */
+ @PreAuthorize("@ss.hasPermi('system:menu:list')")
+ @GetMapping("/list")
+ public AjaxResult list(SysMenu menu)
+ {
+ LoginUser loginUser = SecurityUtils.getLoginUser();
+ Long userId = loginUser.getUserId();
+ List<SysMenu> menus = menuService.selectMenuList(menu, userId);
+ return AjaxResult.success(menus);
+ }
+
+ /**
+ * 根据菜单编号获取详细信息
+ */
+ @PreAuthorize("@ss.hasPermi('system:menu:query')")
+ @GetMapping(value = "/{menuId}")
+ public AjaxResult getInfo(@PathVariable Long menuId)
+ {
+ return AjaxResult.success(menuService.selectMenuById(menuId));
+ }
+
+ /**
+ * 获取菜单下拉树列表
+ */
+ @GetMapping("/treeselect")
+ public AjaxResult treeselect(SysMenu menu)
+ {
+ LoginUser loginUser = SecurityUtils.getLoginUser();
+ Long userId = loginUser.getUserId();
+ List<SysMenu> menus = menuService.selectMenuList(menu, userId);
+ return AjaxResult.success(menuService.buildMenuTreeSelect(menus));
+ }
+
+ /**
+ * 加载对应角色菜单列表树
+ */
+ @GetMapping(value = "/roleMenuTreeselect/{roleId}")
+ public AjaxResult roleMenuTreeselect(@PathVariable("roleId") Long roleId)
+ {
+ LoginUser loginUser = SecurityUtils.getLoginUser();
+ Long userId = loginUser.getUserId();
+ List<SysMenu> menus = menuService.selectMenuList(userId);
+ AjaxResult ajax = AjaxResult.success();
+ ajax.put("checkedKeys", menuService.selectMenuListByRoleId(roleId));
+ ajax.put("menus", menuService.buildMenuTreeSelect(menus));
+ return ajax;
+ }
+
+ /**
+ * 新增菜单
+ */
+ @PreAuthorize("@ss.hasPermi('system:menu:add')")
+ @Log(title = "菜单管理", businessType = BusinessType.INSERT)
+ @PostMapping
+ public AjaxResult add(@Validated @RequestBody 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(@Validated @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));
+ }
+
+ /**
+ * 获取路由信息
+ *
+ * @return 路由信息
+ */
+ @GetMapping("getRouters")
+ public AjaxResult getRouters()
+ {
+ Long userId = SecurityUtils.getLoginUser().getUserId();
+ List<SysMenu> menus = menuService.selectMenuTreeByUserId(userId);
+ return AjaxResult.success(menuService.buildMenus(menus));
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysNoticeController.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysNoticeController.java
new file mode 100644
index 0000000..519fadf
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysNoticeController.java
@@ -0,0 +1,92 @@
+package com.ruoyi.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.RestController;
+import com.ruoyi.common.core.web.controller.BaseController;
+import com.ruoyi.common.core.web.domain.AjaxResult;
+import com.ruoyi.common.core.web.page.TableDataInfo;
+import com.ruoyi.common.log.annotation.Log;
+import com.ruoyi.common.log.enums.BusinessType;
+import com.ruoyi.common.security.utils.SecurityUtils;
+import com.ruoyi.system.domain.SysNotice;
+import com.ruoyi.system.service.ISysNoticeService;
+
+/**
+ * 公告 信息操作处理
+ *
+ * @author ruoyi
+ */
+@RestController
+@RequestMapping("/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.UPDATE)
+ @PostMapping
+ public AjaxResult add(@Validated @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(@Validated @RequestBody SysNotice notice)
+ {
+ notice.setUpdateBy(SecurityUtils.getUsername());
+ return toAjax(noticeService.updateNotice(notice));
+ }
+
+ /**
+ * 删除通知公告
+ */
+ @PreAuthorize("@ss.hasPermi('system:notice:remove')")
+ @Log(title = "通知公告", businessType = BusinessType.DELETE)
+ @DeleteMapping("/{noticeIds}")
+ public AjaxResult remove(@PathVariable Long[] noticeIds)
+ {
+ return toAjax(noticeService.deleteNoticeByIds(noticeIds));
+ }
+}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysOperlogController.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysOperlogController.java
new file mode 100644
index 0000000..98d3491
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysOperlogController.java
@@ -0,0 +1,76 @@
+package com.ruoyi.system.controller;
+
+import java.io.IOException;
+import java.util.List;
+import javax.servlet.http.HttpServletResponse;
+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.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.ruoyi.common.core.utils.poi.ExcelUtil;
+import com.ruoyi.common.core.web.controller.BaseController;
+import com.ruoyi.common.core.web.domain.AjaxResult;
+import com.ruoyi.common.core.web.page.TableDataInfo;
+import com.ruoyi.common.log.annotation.Log;
+import com.ruoyi.common.log.enums.BusinessType;
+import com.ruoyi.system.api.domain.SysOperLog;
+import com.ruoyi.system.service.ISysOperLogService;
+
+/**
+ * 操作日志记录
+ *
+ * @author ruoyi
+ */
+@RestController
+@RequestMapping("/operlog")
+public class SysOperlogController extends BaseController
+{
+ @Autowired
+ private ISysOperLogService operLogService;
+
+ @PreAuthorize("@ss.hasPermi('system:operlog:list')")
+ @GetMapping("/list")
+ public TableDataInfo list(SysOperLog operLog)
+ {
+ startPage();
+ List<SysOperLog> list = operLogService.selectOperLogList(operLog);
+ return getDataTable(list);
+ }
+
+ @Log(title = "操作日志", businessType = BusinessType.EXPORT)
+ @PreAuthorize("@ss.hasPermi('system:operlog:export')")
+ @PostMapping("/export")
+ public void export(HttpServletResponse response, SysOperLog operLog) throws IOException
+ {
+ List<SysOperLog> list = operLogService.selectOperLogList(operLog);
+ ExcelUtil<SysOperLog> util = new ExcelUtil<SysOperLog>(SysOperLog.class);
+ util.exportExcel(response, list, "操作日志");
+ }
+
+ @PreAuthorize("@ss.hasPermi('system:operlog:remove')")
+ @DeleteMapping("/{operIds}")
+ public AjaxResult remove(@PathVariable Long[] operIds)
+ {
+ return toAjax(operLogService.deleteOperLogByIds(operIds));
+ }
+
+ @PreAuthorize("@ss.hasPermi('system:operlog:remove')")
+ @Log(title = "操作日志", businessType = BusinessType.CLEAN)
+ @DeleteMapping("/clean")
+ public AjaxResult clean()
+ {
+ operLogService.cleanOperLog();
+ return AjaxResult.success();
+ }
+
+ @PostMapping
+ public AjaxResult add(@RequestBody SysOperLog operLog)
+ {
+ return toAjax(operLogService.insertOperlog(operLog));
+ }
+}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysPostController.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysPostController.java
new file mode 100644
index 0000000..8e5f15f
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysPostController.java
@@ -0,0 +1,132 @@
+package com.ruoyi.system.controller;
+
+import java.io.IOException;
+import java.util.List;
+import javax.servlet.http.HttpServletResponse;
+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.RestController;
+import com.ruoyi.common.core.constant.UserConstants;
+import com.ruoyi.common.core.utils.poi.ExcelUtil;
+import com.ruoyi.common.core.web.controller.BaseController;
+import com.ruoyi.common.core.web.domain.AjaxResult;
+import com.ruoyi.common.core.web.page.TableDataInfo;
+import com.ruoyi.common.log.annotation.Log;
+import com.ruoyi.common.log.enums.BusinessType;
+import com.ruoyi.common.security.utils.SecurityUtils;
+import com.ruoyi.system.domain.SysPost;
+import com.ruoyi.system.service.ISysPostService;
+
+/**
+ * 岗位信息操作处理
+ *
+ * @author ruoyi
+ */
+@RestController
+@RequestMapping("/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);
+ }
+
+ @Log(title = "岗位管理", businessType = BusinessType.EXPORT)
+ @PreAuthorize("@ss.hasPermi('system:config:export')")
+ @PostMapping("/export")
+ public void export(HttpServletResponse response, SysPost post) throws IOException
+ {
+ List<SysPost> list = postService.selectPostList(post);
+ ExcelUtil<SysPost> util = new ExcelUtil<SysPost>(SysPost.class);
+ util.exportExcel(response, 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(@Validated @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(@Validated @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("/{postIds}")
+ public AjaxResult remove(@PathVariable Long[] postIds)
+ {
+ return toAjax(postService.deletePostByIds(postIds));
+ }
+
+ /**
+ * 获取岗位选择框列表
+ */
+ @GetMapping("/optionselect")
+ public AjaxResult optionselect()
+ {
+ List<SysPost> posts = postService.selectPostAll();
+ return AjaxResult.success(posts);
+ }
+}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysProfileController.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysProfileController.java
new file mode 100644
index 0000000..56f8fbe
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysProfileController.java
@@ -0,0 +1,81 @@
+package com.ruoyi.system.controller;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+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.core.web.controller.BaseController;
+import com.ruoyi.common.core.web.domain.AjaxResult;
+import com.ruoyi.common.log.annotation.Log;
+import com.ruoyi.common.log.enums.BusinessType;
+import com.ruoyi.common.security.utils.SecurityUtils;
+import com.ruoyi.system.api.domain.SysUser;
+import com.ruoyi.system.service.ISysUserService;
+
+/**
+ * 个人信息 业务处理
+ *
+ * @author ruoyi
+ */
+@RestController
+@RequestMapping("/user/profile")
+public class SysProfileController extends BaseController
+{
+ @Autowired
+ private ISysUserService userService;
+
+ /**
+ * 个人信息
+ */
+ @GetMapping
+ public AjaxResult profile()
+ {
+ String username = SecurityUtils.getUsername();
+ SysUser user = userService.selectUserByUserName(username);
+ AjaxResult ajax = AjaxResult.success(user);
+ ajax.put("roleGroup", userService.selectUserRoleGroup(username));
+ ajax.put("postGroup", userService.selectUserPostGroup(username));
+ return ajax;
+ }
+
+ /**
+ * 修改用户
+ */
+ @Log(title = "个人信息", businessType = BusinessType.UPDATE)
+ @PutMapping
+ public AjaxResult updateProfile(@RequestBody SysUser user)
+ {
+ if (userService.updateUserProfile(user) > 0)
+ {
+ return AjaxResult.success();
+ }
+ return AjaxResult.error("修改个人信息异常,请联系管理员");
+ }
+
+ /**
+ * 重置密码
+ */
+ @Log(title = "个人信息", businessType = BusinessType.UPDATE)
+ @PutMapping("/updatePwd")
+ public AjaxResult updatePwd(String oldPassword, String newPassword)
+ {
+ String username = SecurityUtils.getUsername();
+ SysUser user = userService.selectUserByUserName(username);
+ String password = user.getPassword();
+ if (!SecurityUtils.matchesPassword(oldPassword, password))
+ {
+ return AjaxResult.error("修改密码失败,旧密码错误");
+ }
+ if (SecurityUtils.matchesPassword(newPassword, password))
+ {
+ return AjaxResult.error("新密码不能与旧密码相同");
+ }
+ if (userService.resetUserPwd(username, SecurityUtils.encryptPassword(newPassword)) > 0)
+ {
+ return AjaxResult.success();
+ }
+ return AjaxResult.error("修改密码异常,请联系管理员");
+ }
+}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysRoleController.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysRoleController.java
new file mode 100644
index 0000000..857721e
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysRoleController.java
@@ -0,0 +1,156 @@
+package com.ruoyi.system.controller;
+
+import java.io.IOException;
+import java.util.List;
+import javax.servlet.http.HttpServletResponse;
+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.RestController;
+import com.ruoyi.common.core.constant.UserConstants;
+import com.ruoyi.common.core.utils.poi.ExcelUtil;
+import com.ruoyi.common.core.web.controller.BaseController;
+import com.ruoyi.common.core.web.domain.AjaxResult;
+import com.ruoyi.common.core.web.page.TableDataInfo;
+import com.ruoyi.common.log.annotation.Log;
+import com.ruoyi.common.log.enums.BusinessType;
+import com.ruoyi.common.security.utils.SecurityUtils;
+import com.ruoyi.system.api.domain.SysRole;
+import com.ruoyi.system.service.ISysRoleService;
+
+/**
+ * 角色信息
+ *
+ * @author ruoyi
+ */
+@RestController
+@RequestMapping("/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);
+ }
+
+ @Log(title = "角色管理", businessType = BusinessType.EXPORT)
+ @PreAuthorize("@ss.hasPermi('system:role:export')")
+ @PostMapping("/export")
+ public void export(HttpServletResponse response, SysRole role) throws IOException
+ {
+ List<SysRole> list = roleService.selectRoleList(role);
+ ExcelUtil<SysRole> util = new ExcelUtil<SysRole>(SysRole.class);
+ util.exportExcel(response, 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(@Validated @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(@Validated @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("/{roleIds}")
+ public AjaxResult remove(@PathVariable Long[] roleIds)
+ {
+ return toAjax(roleService.deleteRoleByIds(roleIds));
+ }
+
+ /**
+ * 获取角色选择框列表
+ */
+ @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-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysUserController.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysUserController.java
new file mode 100644
index 0000000..6cdfecf
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysUserController.java
@@ -0,0 +1,238 @@
+package com.ruoyi.system.controller;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Set;
+import javax.servlet.http.HttpServletResponse;
+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.RestController;
+import org.springframework.web.multipart.MultipartFile;
+import com.ruoyi.common.core.constant.UserConstants;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.common.core.utils.StringUtils;
+import com.ruoyi.common.core.utils.poi.ExcelUtil;
+import com.ruoyi.common.core.web.controller.BaseController;
+import com.ruoyi.common.core.web.domain.AjaxResult;
+import com.ruoyi.common.core.web.page.TableDataInfo;
+import com.ruoyi.common.log.annotation.Log;
+import com.ruoyi.common.log.enums.BusinessType;
+import com.ruoyi.common.security.utils.SecurityUtils;
+import com.ruoyi.system.api.domain.SysUser;
+import com.ruoyi.system.api.model.UserInfo;
+import com.ruoyi.system.service.ISysPermissionService;
+import com.ruoyi.system.service.ISysPostService;
+import com.ruoyi.system.service.ISysRoleService;
+import com.ruoyi.system.service.ISysUserService;
+
+/**
+ * 用户信息
+ *
+ * @author ruoyi
+ */
+@RestController
+@RequestMapping("/user")
+public class SysUserController extends BaseController
+{
+ @Autowired
+ private ISysUserService userService;
+
+ @Autowired
+ private ISysRoleService roleService;
+
+ @Autowired
+ private ISysPostService postService;
+
+ @Autowired
+ private ISysPermissionService permissionService;
+
+ /**
+ * 获取用户列表
+ */
+ @PreAuthorize("@ss.hasPermi('system:user:list')")
+ @GetMapping("/list")
+ public TableDataInfo list(SysUser user)
+ {
+ startPage();
+ List<SysUser> list = userService.selectUserList(user);
+ return getDataTable(list);
+ }
+
+ @Log(title = "用户管理", businessType = BusinessType.EXPORT)
+ @PreAuthorize("@ss.hasPermi('system:user:export')")
+ @PostMapping("/export")
+ public void export(HttpServletResponse response, SysUser user) throws IOException
+ {
+ List<SysUser> list = userService.selectUserList(user);
+ ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
+ util.exportExcel(response, list, "用户数据");
+ }
+
+ @Log(title = "用户管理", businessType = BusinessType.IMPORT)
+ @PreAuthorize("@ss.hasPermi('system:user:import')")
+ @PostMapping("/importData")
+ public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception
+ {
+ ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
+ List<SysUser> userList = util.importExcel(file.getInputStream());
+ String operName = SecurityUtils.getUsername();
+ String message = userService.importUser(userList, updateSupport, operName);
+ return AjaxResult.success(message);
+ }
+
+ @PostMapping("/importTemplate")
+ public void importTemplate(HttpServletResponse response) throws IOException
+ {
+ ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
+ util.importTemplateExcel(response, "用户数据");
+ }
+
+ /**
+ * 获取当前用户信息
+ */
+ @GetMapping("/info/{username}")
+ public R<UserInfo> info(@PathVariable("username") String username)
+ {
+ SysUser sysUser = userService.selectUserByUserName(username);
+ // 角色集合
+ Set<String> roles = permissionService.getRolePermission(sysUser.getUserId());
+ // 权限集合
+ Set<String> permissions = permissionService.getMenuPermission(sysUser.getUserId());
+ UserInfo sysUserVo = new UserInfo();
+ sysUserVo.setSysUser(sysUser);
+ sysUserVo.setRoles(roles);
+ sysUserVo.setPermissions(permissions);
+ return R.ok(sysUserVo);
+ }
+
+ /**
+ * 获取用户信息
+ *
+ * @return 用户信息
+ */
+ @GetMapping("getInfo")
+ public AjaxResult getInfo()
+ {
+ Long userId = SecurityUtils.getLoginUser().getUserId();
+ // 角色集合
+ Set<String> roles = permissionService.getRolePermission(userId);
+ // 权限集合
+ Set<String> permissions = permissionService.getMenuPermission(userId);
+ AjaxResult ajax = AjaxResult.success();
+ ajax.put("user", userService.selectUserById(userId));
+ ajax.put("roles", roles);
+ ajax.put("permissions", permissions);
+ return ajax;
+ }
+
+ /**
+ * 根据用户编号获取详细信息
+ */
+ @PreAuthorize("@ss.hasPermi('system:user:query')")
+ @GetMapping(value = { "/", "/{userId}" })
+ public AjaxResult getInfo(@PathVariable(value = "userId", required = false) Long userId)
+ {
+ AjaxResult ajax = AjaxResult.success();
+ ajax.put("roles", roleService.selectRoleAll());
+ ajax.put("posts", postService.selectPostAll());
+ if (StringUtils.isNotNull(userId))
+ {
+ ajax.put(AjaxResult.DATA_TAG, 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(@Validated @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(@Validated @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("/{userIds}")
+ public AjaxResult remove(@PathVariable Long[] userIds)
+ {
+ return toAjax(userService.deleteUserByIds(userIds));
+ }
+
+ /**
+ * 重置密码
+ */
+ @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));
+ }
+}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysConfig.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysConfig.java
new file mode 100644
index 0000000..5b3c540
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysConfig.java
@@ -0,0 +1,111 @@
+package com.ruoyi.system.domain;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Size;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.ruoyi.common.core.annotation.Excel;
+import com.ruoyi.common.core.annotation.Excel.ColumnType;
+import com.ruoyi.common.core.web.domain.BaseEntity;
+
+/**
+ * 参数配置表 sys_config
+ *
+ * @author ruoyi
+ */
+public class SysConfig extends BaseEntity
+{
+ private static final long serialVersionUID = 1L;
+
+ /** 参数主键 */
+ @Excel(name = "参数主键", cellType = ColumnType.NUMERIC)
+ private Long configId;
+
+ /** 参数名称 */
+ @Excel(name = "参数名称")
+ private String configName;
+
+ /** 参数键名 */
+ @Excel(name = "参数键名")
+ private String configKey;
+
+ /** 参数键值 */
+ @Excel(name = "参数键值")
+ private String configValue;
+
+ /** 系统内置(Y是 N否) */
+ @Excel(name = "系统内置", readConverterExp = "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;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+ .append("configId", getConfigId())
+ .append("configName", getConfigName())
+ .append("configKey", getConfigKey())
+ .append("configValue", getConfigValue())
+ .append("configType", getConfigType())
+ .append("createBy", getCreateBy())
+ .append("createTime", getCreateTime())
+ .append("updateBy", getUpdateBy())
+ .append("updateTime", getUpdateTime())
+ .append("remark", getRemark())
+ .toString();
+ }
+}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysDictData.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysDictData.java
new file mode 100644
index 0000000..2d5e17d
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysDictData.java
@@ -0,0 +1,176 @@
+package com.ruoyi.system.domain;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Size;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.ruoyi.common.core.annotation.Excel;
+import com.ruoyi.common.core.annotation.Excel.ColumnType;
+import com.ruoyi.common.core.constant.UserConstants;
+import com.ruoyi.common.core.web.domain.BaseEntity;
+
+/**
+ * 字典数据表 sys_dict_data
+ *
+ * @author ruoyi
+ */
+public class SysDictData extends BaseEntity
+{
+ private static final long serialVersionUID = 1L;
+
+ /** 字典编码 */
+ @Excel(name = "字典编码", cellType = ColumnType.NUMERIC)
+ private Long dictCode;
+
+ /** 字典排序 */
+ @Excel(name = "字典排序", cellType = ColumnType.NUMERIC)
+ private Long dictSort;
+
+ /** 字典标签 */
+ @Excel(name = "字典标签")
+ private String dictLabel;
+
+ /** 字典键值 */
+ @Excel(name = "字典键值")
+ private String dictValue;
+
+ /** 字典类型 */
+ @Excel(name = "字典类型")
+ private String dictType;
+
+ /** 样式属性(其他样式扩展) */
+ private String cssClass;
+
+ /** 表格字典样式 */
+ private String listClass;
+
+ /** 是否默认(Y是 N否) */
+ @Excel(name = "是否默认", readConverterExp = "Y=是,N=否")
+ private String isDefault;
+
+ /** 状态(0正常 1停用) */
+ @Excel(name = "状态", readConverterExp = "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;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+ .append("dictCode", getDictCode())
+ .append("dictSort", getDictSort())
+ .append("dictLabel", getDictLabel())
+ .append("dictValue", getDictValue())
+ .append("dictType", getDictType())
+ .append("cssClass", getCssClass())
+ .append("listClass", getListClass())
+ .append("isDefault", getIsDefault())
+ .append("status", getStatus())
+ .append("createBy", getCreateBy())
+ .append("createTime", getCreateTime())
+ .append("updateBy", getUpdateBy())
+ .append("updateTime", getUpdateTime())
+ .append("remark", getRemark())
+ .toString();
+ }
+}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysDictType.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysDictType.java
new file mode 100644
index 0000000..d121388
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysDictType.java
@@ -0,0 +1,94 @@
+package com.ruoyi.system.domain;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Size;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.ruoyi.common.core.annotation.Excel;
+import com.ruoyi.common.core.annotation.Excel.ColumnType;
+import com.ruoyi.common.core.web.domain.BaseEntity;
+
+/**
+ * 字典类型表 sys_dict_type
+ *
+ * @author ruoyi
+ */
+public class SysDictType extends BaseEntity
+{
+ private static final long serialVersionUID = 1L;
+
+ /** 字典主键 */
+ @Excel(name = "字典主键", cellType = ColumnType.NUMERIC)
+ private Long dictId;
+
+ /** 字典名称 */
+ @Excel(name = "字典名称")
+ private String dictName;
+
+ /** 字典类型 */
+ @Excel(name = "字典类型")
+ private String dictType;
+
+ /** 状态(0正常 1停用) */
+ @Excel(name = "状态", readConverterExp = "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;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+ .append("dictId", getDictId())
+ .append("dictName", getDictName())
+ .append("dictType", getDictType())
+ .append("status", getStatus())
+ .append("createBy", getCreateBy())
+ .append("createTime", getCreateTime())
+ .append("updateBy", getUpdateBy())
+ .append("updateTime", getUpdateTime())
+ .append("remark", getRemark())
+ .toString();
+ }
+}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysLogininfor.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysLogininfor.java
new file mode 100644
index 0000000..a03c1a0
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysLogininfor.java
@@ -0,0 +1,144 @@
+package com.ruoyi.system.domain;
+
+import java.util.Date;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.common.core.annotation.Excel;
+import com.ruoyi.common.core.annotation.Excel.ColumnType;
+import com.ruoyi.common.core.web.domain.BaseEntity;
+
+/**
+ * 系统访问记录表 sys_logininfor
+ *
+ * @author ruoyi
+ */
+public class SysLogininfor extends BaseEntity
+{
+ private static final long serialVersionUID = 1L;
+
+ /** ID */
+ @Excel(name = "序号", cellType = ColumnType.NUMERIC)
+ private Long infoId;
+
+ /** 用户账号 */
+ @Excel(name = "用户账号")
+ private String userName;
+
+ /** 登录状态 0成功 1失败 */
+ @Excel(name = "登录状态", readConverterExp = "0=成功,1=失败")
+ private String status;
+
+ /** 登录IP地址 */
+ @Excel(name = "登录地址")
+ private String ipaddr;
+
+ /** 登录地点 */
+ @Excel(name = "登录地点")
+ private String loginLocation;
+
+ /** 浏览器类型 */
+ @Excel(name = "浏览器")
+ private String browser;
+
+ /** 操作系统 */
+ @Excel(name = "操作系统")
+ private String os;
+
+ /** 提示消息 */
+ @Excel(name = "提示消息")
+ private String msg;
+
+ /** 访问时间 */
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ @Excel(name = "访问时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+ 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-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysMenu.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysMenu.java
new file mode 100644
index 0000000..392f6d9
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysMenu.java
@@ -0,0 +1,232 @@
+package com.ruoyi.system.domain;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Size;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+import com.ruoyi.common.core.web.domain.BaseEntity;
+
+/**
+ * 菜单权限表 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;
+
+ /** 菜单状态(0显示 1隐藏) */
+ private String status;
+
+ /** 权限字符串 */
+ 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;
+ }
+
+ @NotBlank(message = "菜单名称不能为空")
+ @Size(min = 0, max = 50, message = "菜单名称长度不能超过50个字符")
+ 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;
+ }
+
+ @NotBlank(message = "显示顺序不能为空")
+ public String getOrderNum()
+ {
+ return orderNum;
+ }
+
+ public void setOrderNum(String orderNum)
+ {
+ this.orderNum = orderNum;
+ }
+
+ @Size(min = 0, max = 200, message = "路由地址不能超过200个字符")
+ public String getPath()
+ {
+ return path;
+ }
+
+ public void setPath(String path)
+ {
+ this.path = path;
+ }
+
+ @Size(min = 0, max = 200, message = "组件路径不能超过255个字符")
+ 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;
+ }
+
+ @NotBlank(message = "菜单类型不能为空")
+ 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 getStatus()
+ {
+ return status;
+ }
+
+ public void setStatus(String status)
+ {
+ this.status = status;
+ }
+
+ @Size(min = 0, max = 100, message = "权限标识长度不能超过100个字符")
+ 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;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+ .append("menuId", getMenuId())
+ .append("menuName", getMenuName())
+ .append("parentId", getParentId())
+ .append("orderNum", getOrderNum())
+ .append("path", getPath())
+ .append("component", getComponent())
+ .append("isFrame", getIsFrame())
+ .append("menuType", getMenuType())
+ .append("visible", getVisible())
+ .append("status ", getStatus())
+ .append("perms", getPerms())
+ .append("icon", getIcon())
+ .append("createBy", getCreateBy())
+ .append("createTime", getCreateTime())
+ .append("updateBy", getUpdateBy())
+ .append("updateTime", getUpdateTime())
+ .append("remark", getRemark())
+ .toString();
+ }
+}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysNotice.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysNotice.java
new file mode 100644
index 0000000..4e1c94c
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysNotice.java
@@ -0,0 +1,101 @@
+package com.ruoyi.system.domain;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Size;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+import com.ruoyi.common.core.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;
+ }
+
+ @NotBlank(message = "公告标题不能为空")
+ @Size(min = 0, max = 50, message = "公告标题不能超过50个字符")
+ 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;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+ .append("noticeId", getNoticeId())
+ .append("noticeTitle", getNoticeTitle())
+ .append("noticeType", getNoticeType())
+ .append("noticeContent", getNoticeContent())
+ .append("status", getStatus())
+ .append("createBy", getCreateBy())
+ .append("createTime", getCreateTime())
+ .append("updateBy", getUpdateBy())
+ .append("updateTime", getUpdateTime())
+ .append("remark", getRemark())
+ .toString();
+ }
+}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysPost.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysPost.java
new file mode 100644
index 0000000..b5a1e9d
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysPost.java
@@ -0,0 +1,123 @@
+package com.ruoyi.system.domain;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Size;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.ruoyi.common.core.annotation.Excel;
+import com.ruoyi.common.core.annotation.Excel.ColumnType;
+import com.ruoyi.common.core.web.domain.BaseEntity;
+
+/**
+ * 岗位表 sys_post
+ *
+ * @author ruoyi
+ */
+public class SysPost extends BaseEntity
+{
+ private static final long serialVersionUID = 1L;
+
+ /** 岗位序号 */
+ @Excel(name = "岗位序号", cellType = ColumnType.NUMERIC)
+ private Long postId;
+
+ /** 岗位编码 */
+ @Excel(name = "岗位编码")
+ private String postCode;
+
+ /** 岗位名称 */
+ @Excel(name = "岗位名称")
+ private String postName;
+
+ /** 岗位排序 */
+ @Excel(name = "岗位排序")
+ private String postSort;
+
+ /** 状态(0正常 1停用) */
+ @Excel(name = "状态", readConverterExp = "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;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+ .append("postId", getPostId())
+ .append("postCode", getPostCode())
+ .append("postName", getPostName())
+ .append("postSort", getPostSort())
+ .append("status", getStatus())
+ .append("createBy", getCreateBy())
+ .append("createTime", getCreateTime())
+ .append("updateBy", getUpdateBy())
+ .append("updateTime", getUpdateTime())
+ .append("remark", getRemark())
+ .toString();
+ }
+}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleDept.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleDept.java
new file mode 100644
index 0000000..8ed8e47
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleDept.java
@@ -0,0 +1,46 @@
+package com.ruoyi.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-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleMenu.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleMenu.java
new file mode 100644
index 0000000..59d2869
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleMenu.java
@@ -0,0 +1,46 @@
+package com.ruoyi.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-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserOnline.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserOnline.java
new file mode 100644
index 0000000..e1495aa
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserOnline.java
@@ -0,0 +1,113 @@
+package com.ruoyi.system.domain;
+
+/**
+ * 当前在线会话
+ *
+ * @author ruoyi
+ */
+public class SysUserOnline
+{
+ /** 会话编号 */
+ private String tokenId;
+
+ /** 部门名称 */
+ private String deptName;
+
+ /** 用户名称 */
+ private String userName;
+
+ /** 登录IP地址 */
+ private String ipaddr;
+
+ /** 登录地址 */
+ private String loginLocation;
+
+ /** 浏览器类型 */
+ private String browser;
+
+ /** 操作系统 */
+ private String os;
+
+ /** 登录时间 */
+ private Long loginTime;
+
+ public String getTokenId()
+ {
+ return tokenId;
+ }
+
+ public void setTokenId(String tokenId)
+ {
+ this.tokenId = tokenId;
+ }
+
+ public String getDeptName()
+ {
+ return deptName;
+ }
+
+ public void setDeptName(String deptName)
+ {
+ this.deptName = deptName;
+ }
+
+ public String getUserName()
+ {
+ return userName;
+ }
+
+ public void setUserName(String userName)
+ {
+ this.userName = userName;
+ }
+
+ 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 Long getLoginTime()
+ {
+ return loginTime;
+ }
+
+ public void setLoginTime(Long loginTime)
+ {
+ this.loginTime = loginTime;
+ }
+}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserPost.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserPost.java
new file mode 100644
index 0000000..07ceb85
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserPost.java
@@ -0,0 +1,46 @@
+package com.ruoyi.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-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserRole.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserRole.java
new file mode 100644
index 0000000..ce2af2b
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserRole.java
@@ -0,0 +1,46 @@
+package com.ruoyi.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-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/MetaVo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/MetaVo.java
new file mode 100644
index 0000000..e83ef10
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/MetaVo.java
@@ -0,0 +1,49 @@
+package com.ruoyi.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-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/RouterVo.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/RouterVo.java
new file mode 100644
index 0000000..21ff6b5
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/RouterVo.java
@@ -0,0 +1,133 @@
+package com.ruoyi.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 boolean 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 boolean getHidden()
+ {
+ return hidden;
+ }
+
+ public void setHidden(boolean 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-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/TreeSelect.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/TreeSelect.java
new file mode 100644
index 0000000..9dc1426
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/TreeSelect.java
@@ -0,0 +1,77 @@
+package com.ruoyi.system.domain.vo;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.stream.Collectors;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.ruoyi.system.api.domain.SysDept;
+import com.ruoyi.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-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysConfigMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysConfigMapper.java
new file mode 100644
index 0000000..2b1219d
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysConfigMapper.java
@@ -0,0 +1,69 @@
+package com.ruoyi.system.mapper;
+
+import java.util.List;
+
+import com.ruoyi.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);
+
+ /**
+ * 批量删除参数信息
+ *
+ * @param configIds 需要删除的参数ID
+ * @return 结果
+ */
+ public int deleteConfigByIds(Long[] configIds);
+}
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDeptMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDeptMapper.java
new file mode 100644
index 0000000..7a2433a
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDeptMapper.java
@@ -0,0 +1,119 @@
+package com.ruoyi.system.mapper;
+
+import java.util.List;
+
+import org.apache.ibatis.annotations.Param;
+
+import com.ruoyi.system.api.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);
+
+ /**
+ * 根据ID查询所有子部门(正常状态)
+ *
+ * @param deptId 部门ID
+ * @return 子部门数
+ */
+ public int selectNormalChildrenDeptById(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-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictDataMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictDataMapper.java
new file mode 100644
index 0000000..f495adf
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictDataMapper.java
@@ -0,0 +1,97 @@
+package com.ruoyi.system.mapper;
+
+import java.util.List;
+
+import org.apache.ibatis.annotations.Param;
+
+import com.ruoyi.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 dictCodes 需要删除的字典数据ID
+ * @return 结果
+ */
+ public int deleteDictDataByIds(Long[] dictCodes);
+
+ /**
+ * 新增字典数据信息
+ *
+ * @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-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictTypeMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictTypeMapper.java
new file mode 100644
index 0000000..1ee37ea
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictTypeMapper.java
@@ -0,0 +1,87 @@
+package com.ruoyi.system.mapper;
+
+import java.util.List;
+
+import org.apache.ibatis.annotations.Mapper;
+
+import com.ruoyi.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 dictIds 需要删除的字典ID
+ * @return 结果
+ */
+ public int deleteDictTypeByIds(Long[] dictIds);
+
+ /**
+ * 新增字典类型信息
+ *
+ * @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-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysLogininforMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysLogininforMapper.java
new file mode 100644
index 0000000..480dd9f
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysLogininforMapper.java
@@ -0,0 +1,42 @@
+package com.ruoyi.system.mapper;
+
+import java.util.List;
+import com.ruoyi.system.domain.SysLogininfor;
+
+/**
+ * 系统访问日志情况信息 数据层
+ *
+ * @author ruoyi
+ */
+public interface SysLogininforMapper
+{
+ /**
+ * 新增系统登录日志
+ *
+ * @param logininfor 访问日志对象
+ */
+ public void insertLogininfor(SysLogininfor logininfor);
+
+ /**
+ * 查询系统登录日志集合
+ *
+ * @param logininfor 访问日志对象
+ * @return 登录记录集合
+ */
+ public List<SysLogininfor> selectLogininforList(SysLogininfor logininfor);
+
+ /**
+ * 批量删除系统登录日志
+ *
+ * @param infoIds 需要删除的登录日志ID
+ * @return 结果
+ */
+ public int deleteLogininforByIds(Long[] infoIds);
+
+ /**
+ * 清空系统登录日志
+ *
+ * @return 结果
+ */
+ public int cleanLogininfor();
+}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java
new file mode 100644
index 0000000..62b545b
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java
@@ -0,0 +1,118 @@
+package com.ruoyi.system.mapper;
+
+import java.util.List;
+
+import org.apache.ibatis.annotations.Param;
+
+import com.ruoyi.system.domain.SysMenu;
+
+/**
+ * 菜单表 数据层
+ *
+ * @author ruoyi
+ */
+public interface SysMenuMapper
+{
+ /**
+ * 查询系统菜单列表
+ *
+ * @param menu 菜单信息
+ * @return 菜单列表
+ */
+ public List<SysMenu> selectMenuList(SysMenu menu);
+
+ /**
+ * 根据用户所有权限
+ *
+ * @return 权限列表
+ */
+ public List<String> selectMenuPerms();
+
+ /**
+ * 根据用户查询系统菜单列表
+ *
+ * @param menu 菜单信息
+ * @return 菜单列表
+ */
+ public List<SysMenu> selectMenuListByUserId(SysMenu menu);
+
+ /**
+ * 根据用户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-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysNoticeMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysNoticeMapper.java
new file mode 100644
index 0000000..5e801ea
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysNoticeMapper.java
@@ -0,0 +1,61 @@
+package com.ruoyi.system.mapper;
+
+import java.util.List;
+
+import com.ruoyi.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);
+
+ /**
+ * 批量删除公告信息
+ *
+ * @param noticeIds 需要删除的公告ID
+ * @return 结果
+ */
+ public int deleteNoticeByIds(Long[] noticeIds);
+}
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOperLogMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOperLogMapper.java
new file mode 100644
index 0000000..5503632
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOperLogMapper.java
@@ -0,0 +1,49 @@
+package com.ruoyi.system.mapper;
+
+import java.util.List;
+
+import com.ruoyi.system.api.domain.SysOperLog;
+
+/**
+ * 操作日志 数据层
+ *
+ * @author ruoyi
+ */
+public interface SysOperLogMapper
+{
+ /**
+ * 新增操作日志
+ *
+ * @param operLog 操作日志对象
+ */
+ public int insertOperlog(SysOperLog operLog);
+
+ /**
+ * 查询系统操作日志集合
+ *
+ * @param operLog 操作日志对象
+ * @return 操作日志集合
+ */
+ public List<SysOperLog> selectOperLogList(SysOperLog operLog);
+
+ /**
+ * 批量删除系统操作日志
+ *
+ * @param operIds 需要删除的操作日志ID
+ * @return 结果
+ */
+ public int deleteOperLogByIds(Long[] operIds);
+
+ /**
+ * 查询操作日志详细
+ *
+ * @param operId 操作ID
+ * @return 操作日志对象
+ */
+ public SysOperLog selectOperLogById(Long operId);
+
+ /**
+ * 清空操作日志
+ */
+ public void cleanOperLog();
+}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysPostMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysPostMapper.java
new file mode 100644
index 0000000..8180998
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysPostMapper.java
@@ -0,0 +1,100 @@
+package com.ruoyi.system.mapper;
+
+import java.util.List;
+
+import com.ruoyi.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 postIds 需要删除的岗位ID
+ * @return 结果
+ */
+ public int deletePostByIds(Long[] postIds);
+
+ /**
+ * 修改岗位信息
+ *
+ * @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-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleDeptMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleDeptMapper.java
new file mode 100644
index 0000000..7946548
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleDeptMapper.java
@@ -0,0 +1,45 @@
+package com.ruoyi.system.mapper;
+
+import java.util.List;
+
+import com.ruoyi.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-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMapper.java
new file mode 100644
index 0000000..68249f4
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMapper.java
@@ -0,0 +1,108 @@
+package com.ruoyi.system.mapper;
+
+import java.util.List;
+
+import com.ruoyi.system.api.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);
+
+ /**
+ * 批量删除角色信息
+ *
+ * @param roleIds 需要删除的角色ID
+ * @return 结果
+ */
+ public int deleteRoleByIds(Long[] roleIds);
+}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMenuMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMenuMapper.java
new file mode 100644
index 0000000..ac01be2
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMenuMapper.java
@@ -0,0 +1,37 @@
+package com.ruoyi.system.mapper;
+
+import java.util.List;
+
+import com.ruoyi.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-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java
new file mode 100644
index 0000000..2df6570
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java
@@ -0,0 +1,113 @@
+package com.ruoyi.system.mapper;
+
+import java.util.List;
+
+import org.apache.ibatis.annotations.Param;
+
+import com.ruoyi.system.api.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 userIds 需要删除的用户ID
+ * @return 结果
+ */
+ public int deleteUserByIds(Long[] userIds);
+
+ /**
+ * 校验用户名称是否唯一
+ *
+ * @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-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserPostMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserPostMapper.java
new file mode 100644
index 0000000..8d50b04
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserPostMapper.java
@@ -0,0 +1,45 @@
+package com.ruoyi.system.mapper;
+
+import java.util.List;
+
+import com.ruoyi.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-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserRoleMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserRoleMapper.java
new file mode 100644
index 0000000..9bce2a0
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserRoleMapper.java
@@ -0,0 +1,64 @@
+package com.ruoyi.system.mapper;
+
+import java.util.List;
+
+import org.apache.ibatis.annotations.Param;
+
+import com.ruoyi.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-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java
new file mode 100644
index 0000000..5f06257
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java
@@ -0,0 +1,77 @@
+package com.ruoyi.system.service;
+
+import java.util.List;
+
+import com.ruoyi.system.domain.SysConfig;
+
+/**
+ * 参数配置 服务层
+ *
+ * @author ruoyi
+ */
+public interface ISysConfigService
+{
+ /**
+ * 查询参数配置信息
+ *
+ * @param configId 参数配置ID
+ * @return 参数配置信息
+ */
+ public SysConfig selectConfigById(Long configId);
+
+ /**
+ * 根据键名查询参数配置信息
+ *
+ * @param configKey 参数键名
+ * @return 参数键值
+ */
+ public String 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 configIds 需要删除的参数ID
+ * @return 结果
+ */
+ public int deleteConfigByIds(Long[] configIds);
+
+ /**
+ * 校验参数键名是否唯一
+ *
+ * @param config 参数信息
+ * @return 结果
+ */
+ public String checkConfigKeyUnique(SysConfig config);
+}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java
new file mode 100644
index 0000000..ee64410
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java
@@ -0,0 +1,110 @@
+package com.ruoyi.system.service;
+
+import java.util.List;
+
+import com.ruoyi.system.api.domain.SysDept;
+import com.ruoyi.system.domain.vo.TreeSelect;
+
+/**
+ * 部门管理 服务层
+ *
+ * @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);
+
+ /**
+ * 根据ID查询所有子部门(正常状态)
+ *
+ * @param deptId 部门ID
+ * @return 子部门数
+ */
+ public int selectNormalChildrenDeptById(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-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictDataService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictDataService.java
new file mode 100644
index 0000000..617fab5
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictDataService.java
@@ -0,0 +1,78 @@
+package com.ruoyi.system.service;
+
+import java.util.List;
+
+import com.ruoyi.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 dictCodes 需要删除的字典数据ID
+ * @return 结果
+ */
+ public int deleteDictDataByIds(Long[] dictCodes);
+
+ /**
+ * 新增保存字典数据信息
+ *
+ * @param dictData 字典数据信息
+ * @return 结果
+ */
+ public int insertDictData(SysDictData dictData);
+
+ /**
+ * 修改保存字典数据信息
+ *
+ * @param dictData 字典数据信息
+ * @return 结果
+ */
+ public int updateDictData(SysDictData dictData);
+}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictTypeService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictTypeService.java
new file mode 100644
index 0000000..90a78b3
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictTypeService.java
@@ -0,0 +1,84 @@
+package com.ruoyi.system.service;
+
+import java.util.List;
+
+import com.ruoyi.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 dictIds 需要删除的字典ID
+ * @return 结果
+ */
+ public int deleteDictTypeByIds(Long[] dictIds);
+
+ /**
+ * 新增保存字典类型信息
+ *
+ * @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-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysLogininforService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysLogininforService.java
new file mode 100644
index 0000000..f77aaa7
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysLogininforService.java
@@ -0,0 +1,40 @@
+package com.ruoyi.system.service;
+
+import java.util.List;
+import com.ruoyi.system.domain.SysLogininfor;
+
+/**
+ * 系统访问日志情况信息 服务层
+ *
+ * @author ruoyi
+ */
+public interface ISysLogininforService
+{
+ /**
+ * 新增系统登录日志
+ *
+ * @param logininfor 访问日志对象
+ */
+ public void insertLogininfor(SysLogininfor logininfor);
+
+ /**
+ * 查询系统登录日志集合
+ *
+ * @param logininfor 访问日志对象
+ * @return 登录记录集合
+ */
+ public List<SysLogininfor> selectLogininforList(SysLogininfor logininfor);
+
+ /**
+ * 批量删除系统登录日志
+ *
+ * @param infoIds 需要删除的登录日志ID
+ * @return
+ */
+ public int deleteLogininforByIds(Long[] infoIds);
+
+ /**
+ * 清空系统登录日志
+ */
+ public void cleanLogininfor();
+}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysMenuService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysMenuService.java
new file mode 100644
index 0000000..96af00e
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysMenuService.java
@@ -0,0 +1,137 @@
+package com.ruoyi.system.service;
+
+import java.util.List;
+import java.util.Set;
+
+import com.ruoyi.system.domain.SysMenu;
+import com.ruoyi.system.domain.vo.RouterVo;
+import com.ruoyi.system.domain.vo.TreeSelect;
+
+/**
+ * 菜单 业务层
+ *
+ * @author ruoyi
+ */
+public interface ISysMenuService
+{
+ /**
+ * 根据用户查询系统菜单列表
+ *
+ * @param userId 用户ID
+ * @return 菜单列表
+ */
+ public List<SysMenu> selectMenuList(Long userId);
+
+ /**
+ * 根据用户查询系统菜单列表
+ *
+ * @param menu 菜单信息
+ * @param userId 用户ID
+ * @return 菜单列表
+ */
+ public List<SysMenu> selectMenuList(SysMenu menu, Long userId);
+
+ /**
+ * 根据用户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-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysNoticeService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysNoticeService.java
new file mode 100644
index 0000000..37c6b45
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysNoticeService.java
@@ -0,0 +1,61 @@
+package com.ruoyi.system.service;
+
+import java.util.List;
+
+import com.ruoyi.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);
+
+ /**
+ * 批量删除公告信息
+ *
+ * @param noticeIds 需要删除的公告ID
+ * @return 结果
+ */
+ public int deleteNoticeByIds(Long[] noticeIds);
+}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOperLogService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOperLogService.java
new file mode 100644
index 0000000..42c94f1
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOperLogService.java
@@ -0,0 +1,49 @@
+package com.ruoyi.system.service;
+
+import java.util.List;
+import com.ruoyi.system.api.domain.SysOperLog;
+
+/**
+ * 操作日志 服务层
+ *
+ * @author ruoyi
+ */
+public interface ISysOperLogService
+{
+ /**
+ * 新增操作日志
+ *
+ * @param operLog 操作日志对象
+ * @return 结果
+ */
+ public int insertOperlog(SysOperLog operLog);
+
+ /**
+ * 查询系统操作日志集合
+ *
+ * @param operLog 操作日志对象
+ * @return 操作日志集合
+ */
+ public List<SysOperLog> selectOperLogList(SysOperLog operLog);
+
+ /**
+ * 批量删除系统操作日志
+ *
+ * @param operIds 需要删除的操作日志ID
+ * @return 结果
+ */
+ public int deleteOperLogByIds(Long[] operIds);
+
+ /**
+ * 查询操作日志详细
+ *
+ * @param operId 操作ID
+ * @return 操作日志对象
+ */
+ public SysOperLog selectOperLogById(Long operId);
+
+ /**
+ * 清空操作日志
+ */
+ public void cleanOperLog();
+}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysPermissionService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysPermissionService.java
new file mode 100644
index 0000000..86c8777
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysPermissionService.java
@@ -0,0 +1,22 @@
+package com.ruoyi.system.service;
+
+import java.util.Set;
+
+public interface ISysPermissionService
+{
+ /**
+ * 获取角色数据权限
+ *
+ * @param user 用户信息
+ * @return 角色权限信息
+ */
+ public Set<String> getRolePermission(Long userId);
+
+ /**
+ * 获取菜单数据权限
+ *
+ * @param user 用户信息
+ * @return 菜单权限信息
+ */
+ public Set<String> getMenuPermission(Long userId);
+}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysPostService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysPostService.java
new file mode 100644
index 0000000..626fe05
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysPostService.java
@@ -0,0 +1,101 @@
+package com.ruoyi.system.service;
+
+import java.util.List;
+
+import com.ruoyi.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);
+
+ /**
+ * 通过岗位ID查询岗位使用数量
+ *
+ * @param postId 岗位ID
+ * @return 结果
+ */
+ public int countUserPostById(Long postId);
+
+ /**
+ * 删除岗位信息
+ *
+ * @param postId 岗位ID
+ * @return 结果
+ */
+ public int deletePostById(Long postId);
+
+ /**
+ * 批量删除岗位信息
+ *
+ * @param postIds 需要删除的岗位ID
+ * @return 结果
+ * @throws Exception 异常
+ */
+ public int deletePostByIds(Long[] postIds);
+
+ /**
+ * 新增保存岗位信息
+ *
+ * @param post 岗位信息
+ * @return 结果
+ */
+ public int insertPost(SysPost post);
+
+ /**
+ * 修改保存岗位信息
+ *
+ * @param post 岗位信息
+ * @return 结果
+ */
+ public int updatePost(SysPost post);
+}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysRoleService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysRoleService.java
new file mode 100644
index 0000000..c3d4afe
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysRoleService.java
@@ -0,0 +1,132 @@
+package com.ruoyi.system.service;
+
+import java.util.List;
+import java.util.Set;
+
+import com.ruoyi.system.api.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);
+
+ /**
+ * 通过角色ID查询角色使用数量
+ *
+ * @param roleId 角色ID
+ * @return 结果
+ */
+ public int countUserRoleByRoleId(Long roleId);
+
+ /**
+ * 新增保存角色信息
+ *
+ * @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);
+
+ /**
+ * 批量删除角色信息
+ *
+ * @param roleIds 需要删除的角色ID
+ * @return 结果
+ */
+ public int deleteRoleByIds(Long[] roleIds);
+}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserOnlineService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserOnlineService.java
new file mode 100644
index 0000000..ec1c168
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserOnlineService.java
@@ -0,0 +1,47 @@
+//package com.ruoyi.system.service;
+//
+//import com.ruoyi.system.domain.SysUserOnline;
+//
+///**
+// * 在线用户 服务层
+// *
+// * @author ruoyi
+// */
+//public interface ISysUserOnlineService
+//{
+// /**
+// * 通过登录地址查询信息
+// *
+// * @param ipaddr 登录地址
+// * @param user 用户信息
+// * @return 在线用户信息
+// */
+// public SysUserOnline selectOnlineByIpaddr(String ipaddr, LoginUser user);
+//
+// /**
+// * 通过用户名称查询信息
+// *
+// * @param userName 用户名称
+// * @param user 用户信息
+// * @return 在线用户信息
+// */
+// public SysUserOnline selectOnlineByUserName(String userName, LoginUser user);
+//
+// /**
+// * 通过登录地址/用户名称查询信息
+// *
+// * @param ipaddr 登录地址
+// * @param userName 用户名称
+// * @param user 用户信息
+// * @return 在线用户信息
+// */
+// public SysUserOnline selectOnlineByInfo(String ipaddr, String userName, LoginUser user);
+//
+// /**
+// * 设置在线用户信息
+// *
+// * @param user 用户信息
+// * @return 在线用户
+// */
+// public SysUserOnline loginUserToUserOnline(LoginUser user);
+//}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java
new file mode 100644
index 0000000..254fc25
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java
@@ -0,0 +1,168 @@
+package com.ruoyi.system.service;
+
+import java.util.List;
+
+import com.ruoyi.system.api.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);
+
+ /**
+ * 批量删除用户信息
+ *
+ * @param userIds 需要删除的用户ID
+ * @return 结果
+ */
+ public int deleteUserByIds(Long[] userIds);
+
+ /**
+ * 导入用户数据
+ *
+ * @param userList 用户数据列表
+ * @param isUpdateSupport 是否更新支持,如果已存在,则进行更新数据
+ * @param operName 操作用户
+ * @return 结果
+ */
+ public String importUser(List<SysUser> userList, Boolean isUpdateSupport, String operName);
+}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java
new file mode 100644
index 0000000..78e1e0e
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java
@@ -0,0 +1,131 @@
+package com.ruoyi.system.service.impl;
+
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.ruoyi.common.core.constant.UserConstants;
+import com.ruoyi.common.core.utils.StringUtils;
+import com.ruoyi.system.domain.SysConfig;
+import com.ruoyi.system.mapper.SysConfigMapper;
+import com.ruoyi.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 String 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 configIds 需要删除的参数ID
+ * @return 结果
+ */
+ @Override
+ public int deleteConfigByIds(Long[] configIds)
+ {
+ return configMapper.deleteConfigByIds(configIds);
+ }
+
+ /**
+ * 校验参数键名是否唯一
+ *
+ * @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-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java
new file mode 100644
index 0000000..ed8b74c
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java
@@ -0,0 +1,308 @@
+package com.ruoyi.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.core.constant.UserConstants;
+import com.ruoyi.common.core.exception.CustomException;
+import com.ruoyi.common.core.utils.StringUtils;
+import com.ruoyi.common.datascope.annotation.DataScope;
+import com.ruoyi.system.api.domain.SysDept;
+import com.ruoyi.system.domain.vo.TreeSelect;
+import com.ruoyi.system.mapper.SysDeptMapper;
+import com.ruoyi.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>();
+ List<Long> tempList = new ArrayList<Long>();
+ for (SysDept dept : depts)
+ {
+ tempList.add(dept.getDeptId());
+ }
+ for (Iterator<SysDept> iterator = depts.iterator(); iterator.hasNext();)
+ {
+ SysDept dept = (SysDept) iterator.next();
+ // 如果是顶级节点, 遍历该父节点的所有子节点
+ if (!tempList.contains(dept.getParentId()))
+ {
+ recursionFn(depts, dept);
+ returnList.add(dept);
+ }
+ }
+ 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);
+ }
+
+ /**
+ * 根据ID查询所有子部门(正常状态)
+ *
+ * @param deptId 部门ID
+ * @return 子部门数
+ */
+ @Override
+ public int selectNormalChildrenDeptById(Long deptId)
+ {
+ return deptMapper.selectNormalChildrenDeptById(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 (StringUtils.isNotNull(n.getParentId()) && 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-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictDataServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictDataServiceImpl.java
new file mode 100644
index 0000000..069a638
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictDataServiceImpl.java
@@ -0,0 +1,118 @@
+package com.ruoyi.system.service.impl;
+
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.ruoyi.system.domain.SysDictData;
+import com.ruoyi.system.mapper.SysDictDataMapper;
+import com.ruoyi.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 dictCodes 需要删除的字典数据ID
+ * @return 结果
+ */
+ public int deleteDictDataByIds(Long[] dictCodes)
+ {
+ return dictDataMapper.deleteDictDataByIds(dictCodes);
+ }
+
+ /**
+ * 新增保存字典数据信息
+ *
+ * @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-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java
new file mode 100644
index 0000000..7684566
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java
@@ -0,0 +1,143 @@
+package com.ruoyi.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.core.constant.UserConstants;
+import com.ruoyi.common.core.utils.StringUtils;
+import com.ruoyi.system.domain.SysDictType;
+import com.ruoyi.system.mapper.SysDictDataMapper;
+import com.ruoyi.system.mapper.SysDictTypeMapper;
+import com.ruoyi.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 dictIds 需要删除的字典ID
+ * @return 结果
+ */
+ public int deleteDictTypeByIds(Long[] dictIds)
+ {
+ return dictTypeMapper.deleteDictTypeByIds(dictIds);
+ }
+
+ /**
+ * 新增保存字典类型信息
+ *
+ * @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-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java
new file mode 100644
index 0000000..f69fd1a
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java
@@ -0,0 +1,65 @@
+package com.ruoyi.system.service.impl;
+
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.ruoyi.system.domain.SysLogininfor;
+import com.ruoyi.system.mapper.SysLogininforMapper;
+import com.ruoyi.system.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 infoIds 需要删除的登录日志ID
+ * @return
+ */
+ @Override
+ public int deleteLogininforByIds(Long[] infoIds)
+ {
+ return logininforMapper.deleteLogininforByIds(infoIds);
+ }
+
+ /**
+ * 清空系统登录日志
+ */
+ @Override
+ public void cleanLogininfor()
+ {
+ logininforMapper.cleanLogininfor();
+ }
+}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java
new file mode 100644
index 0000000..8debbe7
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java
@@ -0,0 +1,383 @@
+package com.ruoyi.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.core.constant.UserConstants;
+import com.ruoyi.common.core.utils.StringUtils;
+import com.ruoyi.system.api.domain.SysUser;
+import com.ruoyi.system.domain.SysMenu;
+import com.ruoyi.system.domain.vo.MetaVo;
+import com.ruoyi.system.domain.vo.RouterVo;
+import com.ruoyi.system.domain.vo.TreeSelect;
+import com.ruoyi.system.mapper.SysMenuMapper;
+import com.ruoyi.system.mapper.SysRoleMenuMapper;
+import com.ruoyi.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 userId 用户ID
+ * @return 菜单列表
+ */
+ @Override
+ public List<SysMenu> selectMenuList(Long userId)
+ {
+ return selectMenuList(new SysMenu(), userId);
+ }
+
+ /**
+ * 查询系统菜单列表
+ *
+ * @param menu 菜单信息
+ * @return 菜单列表
+ */
+ @Override
+ public List<SysMenu> selectMenuList(SysMenu menu, Long userId)
+ {
+ List<SysMenu> menuList = null;
+ // 管理员显示所有菜单信息
+ if (SysUser.isAdmin(userId))
+ {
+ menuList = menuMapper.selectMenuList(menu);
+ }
+ else
+ {
+ menu.getParams().put("userId", userId);
+ menuList = menuMapper.selectMenuListByUserId(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;
+ }
+
+ /**
+ * 根据用户ID查询菜单
+ *
+ * @param userId 用户名称
+ * @return 菜单列表
+ */
+ @Override
+ public List<SysMenu> selectMenuTreeByUserId(Long userId)
+ {
+ List<SysMenu> menus = null;
+ if (SysUser.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.setHidden("1".equals(menu.getVisible()));
+ router.setName(StringUtils.capitalize(menu.getPath()));
+ router.setPath(getRouterPath(menu));
+ router.setComponent(StringUtils.isEmpty(menu.getComponent()) ? "Layout" : menu.getComponent());
+ router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon()));
+ 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-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeServiceImpl.java
new file mode 100644
index 0000000..6ae589c
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeServiceImpl.java
@@ -0,0 +1,93 @@
+package com.ruoyi.system.service.impl;
+
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.ruoyi.system.domain.SysNotice;
+import com.ruoyi.system.mapper.SysNoticeMapper;
+import com.ruoyi.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);
+ }
+
+ /**
+ * 批量删除公告信息
+ *
+ * @param noticeIds 需要删除的公告ID
+ * @return 结果
+ */
+ public int deleteNoticeByIds(Long[] noticeIds)
+ {
+ return noticeMapper.deleteNoticeByIds(noticeIds);
+ }
+}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java
new file mode 100644
index 0000000..24f4322
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java
@@ -0,0 +1,76 @@
+package com.ruoyi.system.service.impl;
+
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.ruoyi.system.api.domain.SysOperLog;
+import com.ruoyi.system.mapper.SysOperLogMapper;
+import com.ruoyi.system.service.ISysOperLogService;
+
+/**
+ * 操作日志 服务层处理
+ *
+ * @author ruoyi
+ */
+@Service
+public class SysOperLogServiceImpl implements ISysOperLogService
+{
+ @Autowired
+ private SysOperLogMapper operLogMapper;
+
+ /**
+ * 新增操作日志
+ *
+ * @param operLog 操作日志对象
+ * @return 结果
+ */
+ @Override
+ public int insertOperlog(SysOperLog operLog)
+ {
+ return operLogMapper.insertOperlog(operLog);
+ }
+
+ /**
+ * 查询系统操作日志集合
+ *
+ * @param operLog 操作日志对象
+ * @return 操作日志集合
+ */
+ @Override
+ public List<SysOperLog> selectOperLogList(SysOperLog operLog)
+ {
+ return operLogMapper.selectOperLogList(operLog);
+ }
+
+ /**
+ * 批量删除系统操作日志
+ *
+ * @param operIds 需要删除的操作日志ID
+ * @return 结果
+ */
+ public int deleteOperLogByIds(Long[] operIds)
+ {
+ return operLogMapper.deleteOperLogByIds(operIds);
+ }
+
+ /**
+ * 查询操作日志详细
+ *
+ * @param operId 操作ID
+ * @return 操作日志对象
+ */
+ @Override
+ public SysOperLog selectOperLogById(Long operId)
+ {
+ return operLogMapper.selectOperLogById(operId);
+ }
+
+ /**
+ * 清空操作日志
+ */
+ @Override
+ public void cleanOperLog()
+ {
+ operLogMapper.cleanOperLog();
+ }
+}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysPermissionServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysPermissionServiceImpl.java
new file mode 100644
index 0000000..f8c1676
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysPermissionServiceImpl.java
@@ -0,0 +1,63 @@
+package com.ruoyi.system.service.impl;
+
+import java.util.HashSet;
+import java.util.Set;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.ruoyi.system.api.domain.SysUser;
+import com.ruoyi.system.service.ISysMenuService;
+import com.ruoyi.system.service.ISysPermissionService;
+import com.ruoyi.system.service.ISysRoleService;
+
+@Service
+public class SysPermissionServiceImpl implements ISysPermissionService
+{
+ @Autowired
+ private ISysRoleService roleService;
+
+ @Autowired
+ private ISysMenuService menuService;
+
+ /**
+ * 获取角色数据权限
+ *
+ * @param user 用户信息
+ * @return 角色权限信息
+ */
+ public Set<String> getRolePermission(Long userId)
+ {
+ Set<String> roles = new HashSet<String>();
+ // 管理员拥有所有权限
+ if (SysUser.isAdmin(userId))
+ {
+ roles.add("admin");
+ }
+ else
+ {
+ roles.addAll(roleService.selectRolePermissionByUserId(userId));
+ }
+ return roles;
+ }
+
+ /**
+ * 获取菜单数据权限
+ *
+ * @param user 用户信息
+ * @return 菜单权限信息
+ */
+ public Set<String> getMenuPermission(Long userId)
+ {
+ Set<String> perms = new HashSet<String>();
+ // 管理员拥有所有权限
+ if (SysUser.isAdmin(userId))
+ {
+ perms.add("*:*:*");
+ }
+ else
+ {
+ perms.addAll(menuService.selectMenuPermsByUserId(userId));
+ }
+ return perms;
+ }
+}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysPostServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysPostServiceImpl.java
new file mode 100644
index 0000000..315e2f0
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysPostServiceImpl.java
@@ -0,0 +1,179 @@
+package com.ruoyi.system.service.impl;
+
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.ruoyi.common.core.constant.UserConstants;
+import com.ruoyi.common.core.exception.CustomException;
+import com.ruoyi.common.core.utils.StringUtils;
+import com.ruoyi.system.domain.SysPost;
+import com.ruoyi.system.mapper.SysPostMapper;
+import com.ruoyi.system.mapper.SysUserPostMapper;
+import com.ruoyi.system.service.ISysPostService;
+
+/**
+ * 岗位信息 服务层处理
+ *
+ * @author ruoyi
+ */
+@Service
+public class SysPostServiceImpl implements ISysPostService
+{
+ @Autowired
+ private SysPostMapper postMapper;
+
+ @Autowired
+ private SysUserPostMapper userPostMapper;
+
+ /**
+ * 查询岗位信息集合
+ *
+ * @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;
+ }
+
+ /**
+ * 通过岗位ID查询岗位使用数量
+ *
+ * @param postId 岗位ID
+ * @return 结果
+ */
+ @Override
+ public int countUserPostById(Long postId)
+ {
+ return userPostMapper.countUserPostById(postId);
+ }
+
+ /**
+ * 删除岗位信息
+ *
+ * @param postId 岗位ID
+ * @return 结果
+ */
+ @Override
+ public int deletePostById(Long postId)
+ {
+ return postMapper.deletePostById(postId);
+ }
+
+ /**
+ * 批量删除岗位信息
+ *
+ * @param postIds 需要删除的岗位ID
+ * @return 结果
+ * @throws Exception 异常
+ */
+ public int deletePostByIds(Long[] postIds)
+ {
+ for (Long postId : postIds)
+ {
+ SysPost post = selectPostById(postId);
+ if (countUserPostById(postId) > 0)
+ {
+ throw new CustomException(String.format("%1$s已分配,不能删除", post.getPostName()));
+ }
+ }
+ return postMapper.deletePostByIds(postIds);
+ }
+
+ /**
+ * 新增保存岗位信息
+ *
+ * @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-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java
new file mode 100644
index 0000000..d320536
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java
@@ -0,0 +1,312 @@
+package com.ruoyi.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.core.constant.UserConstants;
+import com.ruoyi.common.core.exception.CustomException;
+import com.ruoyi.common.core.utils.SpringUtils;
+import com.ruoyi.common.core.utils.StringUtils;
+import com.ruoyi.common.datascope.annotation.DataScope;
+import com.ruoyi.system.api.domain.SysRole;
+import com.ruoyi.system.domain.SysRoleDept;
+import com.ruoyi.system.domain.SysRoleMenu;
+import com.ruoyi.system.mapper.SysRoleDeptMapper;
+import com.ruoyi.system.mapper.SysRoleMapper;
+import com.ruoyi.system.mapper.SysRoleMenuMapper;
+import com.ruoyi.system.mapper.SysUserRoleMapper;
+import com.ruoyi.system.service.ISysRoleService;
+
+/**
+ * 角色 业务层处理
+ *
+ * @author ruoyi
+ */
+@Service
+public class SysRoleServiceImpl implements ISysRoleService
+{
+ @Autowired
+ private SysRoleMapper roleMapper;
+
+ @Autowired
+ private SysRoleMenuMapper roleMenuMapper;
+
+ @Autowired
+ private SysUserRoleMapper userRoleMapper;
+
+ @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 SpringUtils.getAopProxy(this).selectRoleList(new SysRole());
+ }
+
+ /**
+ * 根据用户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("不允许操作超级管理员角色");
+ }
+ }
+
+ /**
+ * 通过角色ID查询角色使用数量
+ *
+ * @param roleId 角色ID
+ * @return 结果
+ */
+ @Override
+ public int countUserRoleByRoleId(Long roleId)
+ {
+ return userRoleMapper.countUserRoleByRoleId(roleId);
+ }
+
+ /**
+ * 新增保存角色信息
+ *
+ * @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);
+ }
+
+ /**
+ * 批量删除角色信息
+ *
+ * @param roleIds 需要删除的角色ID
+ * @return 结果
+ */
+ public int deleteRoleByIds(Long[] roleIds)
+ {
+ for (Long roleId : roleIds)
+ {
+ checkRoleAllowed(new SysRole(roleId));
+ SysRole role = selectRoleById(roleId);
+ if (countUserRoleByRoleId(roleId) > 0)
+ {
+ throw new CustomException(String.format("%1$s已分配,不能删除", role.getRoleName()));
+ }
+ }
+ return roleMapper.deleteRoleByIds(roleIds);
+ }
+}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserOnlineServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserOnlineServiceImpl.java
new file mode 100644
index 0000000..f15db5a
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserOnlineServiceImpl.java
@@ -0,0 +1,95 @@
+//package com.ruoyi.system.service.impl;
+//
+//import org.springframework.stereotype.Service;
+//
+//import com.ruoyi.common.core.utils.StringUtils;
+//import com.ruoyi.system.domain.SysUserOnline;
+//import com.ruoyi.system.service.ISysUserOnlineService;
+//
+///**
+// * 在线用户 服务层处理
+// *
+// * @author ruoyi
+// */
+//@Service
+//public class SysUserOnlineServiceImpl implements ISysUserOnlineService
+//{
+// /**
+// * 通过登录地址查询信息
+// *
+// * @param ipaddr 登录地址
+// * @param user 用户信息
+// * @return 在线用户信息
+// */
+// @Override
+// public SysUserOnline selectOnlineByIpaddr(String ipaddr, LoginUser user)
+// {
+// if (StringUtils.equals(ipaddr, user.getIpaddr()))
+// {
+// return loginUserToUserOnline(user);
+// }
+// return null;
+// }
+//
+// /**
+// * 通过用户名称查询信息
+// *
+// * @param userName 用户名称
+// * @param user 用户信息
+// * @return 在线用户信息
+// */
+// @Override
+// public SysUserOnline selectOnlineByUserName(String userName, LoginUser user)
+// {
+// if (StringUtils.equals(userName, user.getUsername()))
+// {
+// return loginUserToUserOnline(user);
+// }
+// return null;
+// }
+//
+// /**
+// * 通过登录地址/用户名称查询信息
+// *
+// * @param ipaddr 登录地址
+// * @param userName 用户名称
+// * @param user 用户信息
+// * @return 在线用户信息
+// */
+// @Override
+// public SysUserOnline selectOnlineByInfo(String ipaddr, String userName, LoginUser user)
+// {
+// if (StringUtils.equals(ipaddr, user.getIpaddr()) && StringUtils.equals(userName, user.getUsername()))
+// {
+// return loginUserToUserOnline(user);
+// }
+// return null;
+// }
+//
+// /**
+// * 设置在线用户信息
+// *
+// * @param user 用户信息
+// * @return 在线用户
+// */
+// public SysUserOnline loginUserToUserOnline(LoginUser user)
+// {
+// if (StringUtils.isNull(user) && StringUtils.isNull(user.getUser()))
+// {
+// return null;
+// }
+// SysUserOnline sysUserOnline = new SysUserOnline();
+// sysUserOnline.setTokenId(user.getToken());
+// sysUserOnline.setUserName(user.getUsername());
+// sysUserOnline.setIpaddr(user.getIpaddr());
+// sysUserOnline.setLoginLocation(user.getLoginLocation());
+// sysUserOnline.setBrowser(user.getBrowser());
+// sysUserOnline.setOs(user.getOs());
+// sysUserOnline.setLoginTime(user.getLoginTime());
+// if (StringUtils.isNotNull(user.getUser().getDept()))
+// {
+// sysUserOnline.setDeptName(user.getUser().getDept().getDeptName());
+// }
+// return sysUserOnline;
+// }
+//}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java
new file mode 100644
index 0000000..fd22d33
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java
@@ -0,0 +1,457 @@
+package com.ruoyi.system.service.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.ruoyi.common.core.constant.UserConstants;
+import com.ruoyi.common.core.exception.CustomException;
+import com.ruoyi.common.core.utils.StringUtils;
+import com.ruoyi.common.datascope.annotation.DataScope;
+import com.ruoyi.common.security.utils.SecurityUtils;
+import com.ruoyi.system.api.domain.SysRole;
+import com.ruoyi.system.api.domain.SysUser;
+import com.ruoyi.system.domain.SysPost;
+import com.ruoyi.system.domain.SysUserPost;
+import com.ruoyi.system.domain.SysUserRole;
+import com.ruoyi.system.mapper.SysPostMapper;
+import com.ruoyi.system.mapper.SysRoleMapper;
+import com.ruoyi.system.mapper.SysUserMapper;
+import com.ruoyi.system.mapper.SysUserPostMapper;
+import com.ruoyi.system.mapper.SysUserRoleMapper;
+import com.ruoyi.system.service.ISysConfigService;
+import com.ruoyi.system.service.ISysUserService;
+
+/**
+ * 用户 业务层处理
+ *
+ * @author ruoyi
+ */
+@Service
+public class SysUserServiceImpl implements ISysUserService
+{
+ private static final Logger log = LoggerFactory.getLogger(SysUserServiceImpl.class);
+
+ @Autowired
+ private SysUserMapper userMapper;
+
+ @Autowired
+ private SysRoleMapper roleMapper;
+
+ @Autowired
+ private SysPostMapper postMapper;
+
+ @Autowired
+ private SysUserRoleMapper userRoleMapper;
+
+ @Autowired
+ private SysUserPostMapper userPostMapper;
+
+ @Autowired
+ private ISysConfigService configService;
+
+ /**
+ * 根据条件分页查询用户列表
+ *
+ * @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);
+ }
+
+ /**
+ * 批量删除用户信息
+ *
+ * @param userIds 需要删除的用户ID
+ * @return 结果
+ */
+ public int deleteUserByIds(Long[] userIds)
+ {
+ for (Long userId : userIds)
+ {
+ checkUserAllowed(new SysUser(userId));
+ }
+ return userMapper.deleteUserByIds(userIds);
+ }
+
+ /**
+ * 导入用户数据
+ *
+ * @param userList 用户数据列表
+ * @param isUpdateSupport 是否更新支持,如果已存在,则进行更新数据
+ * @param operName 操作用户
+ * @return 结果
+ */
+ @Override
+ public String importUser(List<SysUser> userList, Boolean isUpdateSupport, String operName)
+ {
+ if (StringUtils.isNull(userList) || userList.size() == 0)
+ {
+ throw new CustomException("导入用户数据不能为空!");
+ }
+ int successNum = 0;
+ int failureNum = 0;
+ StringBuilder successMsg = new StringBuilder();
+ StringBuilder failureMsg = new StringBuilder();
+ String password = configService.selectConfigByKey("sys.user.initPassword");
+ for (SysUser user : userList)
+ {
+ try
+ {
+ // 验证是否存在这个用户
+ SysUser u = userMapper.selectUserByUserName(user.getUserName());
+ if (StringUtils.isNull(u))
+ {
+ user.setPassword(SecurityUtils.encryptPassword(password));
+ user.setCreateBy(operName);
+ this.insertUser(user);
+ successNum++;
+ successMsg.append("<br/>" + successNum + "、账号 " + user.getUserName() + " 导入成功");
+ }
+ else if (isUpdateSupport)
+ {
+ user.setUpdateBy(operName);
+ this.updateUser(user);
+ successNum++;
+ successMsg.append("<br/>" + successNum + "、账号 " + user.getUserName() + " 更新成功");
+ }
+ else
+ {
+ failureNum++;
+ failureMsg.append("<br/>" + failureNum + "、账号 " + user.getUserName() + " 已存在");
+ }
+ }
+ catch (Exception e)
+ {
+ failureNum++;
+ String msg = "<br/>" + failureNum + "、账号 " + user.getUserName() + " 导入失败:";
+ failureMsg.append(msg + e.getMessage());
+ log.error(msg, e);
+ }
+ }
+ if (failureNum > 0)
+ {
+ failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:");
+ throw new CustomException(failureMsg.toString());
+ }
+ else
+ {
+ successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:");
+ }
+ return successMsg.toString();
+ }
+
+}
diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/banner.txt b/ruoyi-modules/ruoyi-system/src/main/resources/banner.txt
new file mode 100644
index 0000000..e5e8395
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/resources/banner.txt
@@ -0,0 +1,10 @@
+Spring Boot Version: ${spring-boot.version}
+Spring Application Name: ${spring.application.name}
+ _ _
+ (_) | |
+ _ __ _ _ ___ _ _ _ ______ ___ _ _ ___ | |_ ___ _ __ ___
+| '__|| | | | / _ \ | | | || ||______|/ __|| | | |/ __|| __| / _ \| '_ ` _ \
+| | | |_| || (_) || |_| || | \__ \| |_| |\__ \| |_ | __/| | | | | |
+|_| \__,_| \___/ \__, ||_| |___/ \__, ||___/ \__| \___||_| |_| |_|
+ __/ | __/ |
+ |___/ |___/
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/bootstrap.yml b/ruoyi-modules/ruoyi-system/src/main/resources/bootstrap.yml
new file mode 100644
index 0000000..554d797
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/resources/bootstrap.yml
@@ -0,0 +1,24 @@
+# Tomcat
+server:
+ port: 9201
+
+# Spring
+spring:
+ application:
+ # 应用名称
+ name: ruoyi-system
+ profiles:
+ # 环境配置
+ active: dev
+ cloud:
+ nacos:
+ discovery:
+ # 服务注册地址
+ server-addr: 127.0.0.1:8848
+ config:
+ # 配置中心地址
+ server-addr: 127.0.0.1:8848
+ # 配置文件格式
+ file-extension: yml
+ # 共享配置
+ shared-dataids: application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/logback.xml b/ruoyi-modules/ruoyi-system/src/main/resources/logback.xml
new file mode 100644
index 0000000..ebd3680
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/resources/logback.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration scan="true" scanPeriod="60 seconds" debug="false">
+ <!-- 日志存放路径 -->
+ <property name="log.path" value="/home/ruoyi/logs/ruoyi-system" />
+ <!-- 日志输出格式 -->
+ <property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />:ss} %-5level ${springAppName:-} %thread %logger %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}/info.log</file>
+ <!-- 循环政策:基于时间创建日志文件 -->
+ <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+ <!-- 日志文件名格式 -->
+ <fileNamePattern>${log.path}/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}/error.log</file>
+ <!-- 循环政策:基于时间创建日志文件 -->
+ <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+ <!-- 日志文件名格式 -->
+ <fileNamePattern>${log.path}/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>
+
+ <!-- 系统模块日志级别控制 -->
+ <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>
+</configuration>
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysConfigMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysConfigMapper.xml
new file mode 100644
index 0000000..2f0c8be
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysConfigMapper.xml
@@ -0,0 +1,112 @@
+<?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.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="beginTime != null and beginTime != ''"><!-- 开始时间检索 -->
+ and date_format(create_time,'%y%m%d') >= date_format(#{beginTime},'%y%m%d')
+ </if>
+ <if test="endTime != null and endTime != ''"><!-- 结束时间检索 -->
+ and date_format(create_time,'%y%m%d') <= date_format(#{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>
+
+ <delete id="deleteConfigByIds" parameterType="Long">
+ delete from sys_config where config_id in
+ <foreach item="configId" collection="array" open="(" separator="," close=")">
+ #{configId}
+ </foreach>
+ </delete>
+
+</mapper>
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml
new file mode 100644
index 0000000..cda58d7
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml
@@ -0,0 +1,155 @@
+<?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.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="selectNormalChildrenDeptById" parameterType="Long" resultType="java.lang.Integer">
+ select count(*) from sys_dept where status = 0 and del_flag = '0' and 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-modules/ruoyi-system/src/main/resources/mapper/system/SysDictDataMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDictDataMapper.xml
new file mode 100644
index 0000000..a898122
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDictDataMapper.xml
@@ -0,0 +1,123 @@
+<?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.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>
+
+ <delete id="deleteDictDataByIds" parameterType="Long">
+ delete from sys_dict_data where dict_code in
+ <foreach collection="array" item="dictCode" open="(" separator="," close=")">
+ #{dictCode}
+ </foreach>
+ </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-modules/ruoyi-system/src/main/resources/mapper/system/SysDictTypeMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDictTypeMapper.xml
new file mode 100644
index 0000000..cf7fc06
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDictTypeMapper.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.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="beginTime != null and beginTime != ''"><!-- 开始时间检索 -->
+ and date_format(create_time,'%y%m%d') >= date_format(#{beginTime},'%y%m%d')
+ </if>
+ <if test="endTime != null and endTime != ''"><!-- 结束时间检索 -->
+ and date_format(create_time,'%y%m%d') <= date_format(#{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>
+
+ <delete id="deleteDictTypeByIds" parameterType="Long">
+ delete from sys_dict_type where dict_id in
+ <foreach collection="array" item="dictId" open="(" separator="," close=")">
+ #{dictId}
+ </foreach>
+ </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-modules/ruoyi-system/src/main/resources/mapper/system/SysLogininforMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysLogininforMapper.xml
new file mode 100644
index 0000000..6f0a241
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysLogininforMapper.xml
@@ -0,0 +1,57 @@
+<?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.system.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="beginTime != null and beginTime != ''"><!-- 开始时间检索 -->
+ and date_format(login_time,'%y%m%d') >= date_format(#{beginTime},'%y%m%d')
+ </if>
+ <if test="endTime != null and endTime != ''"><!-- 结束时间检索 -->
+ and date_format(login_time,'%y%m%d') <= date_format(#{endTime},'%y%m%d')
+ </if>
+ </where>
+ order by info_id desc
+ </select>
+
+ <delete id="deleteLogininforByIds" parameterType="Long">
+ 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-modules/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml
new file mode 100644
index 0000000..7c97ff5
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml
@@ -0,0 +1,185 @@
+<?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.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="status" column="status" />
+ <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, status, 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>
+ <if test="status != null and status != ''">
+ AND status = #{status}
+ </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, m.status, 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.status = 0
+ order by m.parent_id, m.order_num
+ </select>
+
+ <select id="selectMenuListByUserId" parameterType="SysMenu" resultMap="SysMenuResult">
+ select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.visible, m.status, 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
+ where ur.user_id = #{params.userId}
+ <if test="menuName != null and menuName != ''">
+ AND menu_name like concat('%', #{menuName}, '%')
+ </if>
+ <if test="visible != null and visible != ''">
+ AND visible = #{visible}
+ </if>
+ <if test="status != null and status != ''">
+ AND status = #{status}
+ </if>
+ 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, m.status, 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.status = 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
+ left join sys_role r on r.role_id = ur.role_id
+ where m.status = '0' and r.status = '0' and 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">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="status != null">status = #{status},</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="status != null">status,</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="status != null">#{status},</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-modules/ruoyi-system/src/main/resources/mapper/system/SysNoticeMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysNoticeMapper.xml
new file mode 100644
index 0000000..76caf2d
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysNoticeMapper.xml
@@ -0,0 +1,89 @@
+<?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.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>
+
+ <delete id="deleteNoticeByIds" parameterType="Long">
+ delete from sys_notice where notice_id in
+ <foreach item="noticeId" collection="array" open="(" separator="," close=")">
+ #{noticeId}
+ </foreach>
+ </delete>
+
+</mapper>
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysOperLogMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysOperLogMapper.xml
new file mode 100644
index 0000000..e70b7bf
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysOperLogMapper.xml
@@ -0,0 +1,83 @@
+<?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.system.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 businessType != ''">
+ 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="beginTime != null and beginTime != ''"><!-- 开始时间检索 -->
+ and date_format(oper_time,'%y%m%d') >= date_format(#{beginTime},'%y%m%d')
+ </if>
+ <if test="endTime != null and endTime != ''"><!-- 结束时间检索 -->
+ and date_format(oper_time,'%y%m%d') <= date_format(#{endTime},'%y%m%d')
+ </if>
+ </where>
+ order by oper_id desc
+ </select>
+
+ <delete id="deleteOperLogByIds" parameterType="Long">
+ 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-modules/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml
new file mode 100644
index 0000000..e845b0a
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml
@@ -0,0 +1,122 @@
+<?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.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>
+
+ <delete id="deletePostByIds" parameterType="Long">
+ delete from sys_post where post_id in
+ <foreach collection="array" item="postId" open="(" separator="," close=")">
+ #{postId}
+ </foreach>
+ </delete>
+
+</mapper>
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml
new file mode 100644
index 0000000..700671e
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/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.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-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml
new file mode 100644
index 0000000..d007955
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml
@@ -0,0 +1,145 @@
+<?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.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="beginTime != null and beginTime != ''"><!-- 开始时间检索 -->
+ and date_format(r.create_time,'%y%m%d') >= date_format(#{beginTime},'%y%m%d')
+ </if>
+ <if test="endTime != null and endTime != ''"><!-- 结束时间检索 -->
+ and date_format(r.create_time,'%y%m%d') <= date_format(#{endTime},'%y%m%d')
+ </if>
+ <!-- 数据范围过滤 -->
+ ${dataScope}
+ order by r.role_sort
+ </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>
+
+ <delete id="deleteRoleByIds" parameterType="Long">
+ update sys_role set del_flag = '2' where role_id in
+ <foreach collection="array" item="roleId" open="(" separator="," close=")">
+ #{roleId}
+ </foreach>
+ </delete>
+
+</mapper>
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml
new file mode 100644
index 0000000..5f6c047
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/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.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-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml
new file mode 100644
index 0000000..af843eb
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml
@@ -0,0 +1,182 @@
+<?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.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">
+ select u.user_id, u.dept_id, u.nick_name, u.user_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_name, d.leader from sys_user u
+ left join sys_dept d on u.dept_id = d.dept_id
+ 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="beginTime != null and beginTime != ''"><!-- 开始时间检索 -->
+ AND date_format(u.create_time,'%y%m%d') >= date_format(#{beginTime},'%y%m%d')
+ </if>
+ <if test="endTime != null and endTime != ''"><!-- 结束时间检索 -->
+ AND date_format(u.create_time,'%y%m%d') <= date_format(#{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>
+
+ <delete id="deleteUserByIds" parameterType="Long">
+ update sys_user set del_flag = '2' where user_id in
+ <foreach collection="array" item="userId" open="(" separator="," close=")">
+ #{userId}
+ </foreach>
+ </delete>
+
+</mapper>
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserPostMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserPostMapper.xml
new file mode 100644
index 0000000..21c4098
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/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.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-modules/ruoyi-system/src/main/resources/mapper/system/SysUserRoleMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserRoleMapper.xml
new file mode 100644
index 0000000..95e07ad
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/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.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
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/build.bat b/ruoyi-ui/bin/build.bat
new file mode 100644
index 0000000..3159093
--- /dev/null
+++ b/ruoyi-ui/bin/build.bat
@@ -0,0 +1,12 @@
+@echo off
+echo.
+echo [��Ϣ] ���Web���̣�����dist�ļ���
+echo.
+
+%~d0
+cd %~dp0
+
+cd ..
+npm run build:prod
+
+pause
\ No newline at end of file
diff --git a/ruoyi-ui/bin/package.bat b/ruoyi-ui/bin/package.bat
new file mode 100644
index 0000000..5d9fca5
--- /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..62c2bdd
--- /dev/null
+++ b/ruoyi-ui/package.json
@@ -0,0 +1,109 @@
+{
+ "name": "ruoyi",
+ "version": "2.2.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.4.0",
+ "axios": "0.18.1",
+ "clipboard": "2.0.4",
+ "echarts": "4.2.1",
+ "element-ui": "2.13.0",
+ "file-saver": "2.0.1",
+ "js-beautify": "^1.10.2",
+ "fuse.js": "3.4.4",
+ "js-cookie": "2.2.0",
+ "jsencrypt": "3.0.0-rc.1",
+ "normalize.css": "7.0.0",
+ "nprogress": "0.2.0",
+ "path-to-regexp": "2.4.0",
+ "screenfull": "4.2.0",
+ "sortablejs": "1.8.4",
+ "vue": "2.6.10",
+ "vue-count-to": "1.0.13",
+ "vue-quill-editor": "3.0.6",
+ "vue-cropper": "0.4.9",
+ "vue-router": "3.0.2",
+ "vue-splitpane": "1.0.4",
+ "vuedraggable": "2.20.0",
+ "vuex": "3.1.0"
+ },
+ "devDependencies": {
+ "@babel/core": "7.0.0",
+ "@babel/register": "7.0.0",
+ "@babel/parser": "^7.7.4",
+ "@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..121ffe2
--- /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..9aa93ca
--- /dev/null
+++ b/ruoyi-ui/src/api/login.js
@@ -0,0 +1,39 @@
+import request from '@/utils/request'
+
+const client_id = 'web'
+const client_secret = '123456'
+const grant_type = 'password'
+const scope = 'server'
+
+// 登录方法
+export function login(username, password, code, uuid) {
+ return request({
+ url: '/auth/oauth/token',
+ method: 'post',
+ params: { username, password, code, uuid, client_id, client_secret, grant_type, scope }
+ })
+}
+
+// 获取用户详细信息
+export function getInfo() {
+ return request({
+ url: '/system/user/getInfo',
+ method: 'get'
+ })
+}
+
+// 退出方法
+export function logout() {
+ return request({
+ url: '/auth/token/logout',
+ method: 'delete'
+ })
+}
+
+// 获取验证码
+export function getCodeImg() {
+ return request({
+ url: '/code',
+ 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..ec9511d
--- /dev/null
+++ b/ruoyi-ui/src/api/menu.js
@@ -0,0 +1,9 @@
+import request from '@/utils/request'
+
+// 获取路由
+export const getRouters = () => {
+ return request({
+ url: '/system/menu/getRouters',
+ method: 'get'
+ })
+}
\ No newline at end of file
diff --git a/ruoyi-ui/src/api/monitor/job.js b/ruoyi-ui/src/api/monitor/job.js
new file mode 100644
index 0000000..3ee56b2
--- /dev/null
+++ b/ruoyi-ui/src/api/monitor/job.js
@@ -0,0 +1,71 @@
+import request from '@/utils/request'
+
+// 查询定时任务调度列表
+export function listJob(query) {
+ return request({
+ url: '/schedule/job/list',
+ method: 'get',
+ params: query
+ })
+}
+
+// 查询定时任务调度详细
+export function getJob(jobId) {
+ return request({
+ url: '/schedule/job/' + jobId,
+ method: 'get'
+ })
+}
+
+// 新增定时任务调度
+export function addJob(data) {
+ return request({
+ url: '/schedule/job',
+ method: 'post',
+ data: data
+ })
+}
+
+// 修改定时任务调度
+export function updateJob(data) {
+ return request({
+ url: '/schedule/job',
+ method: 'put',
+ data: data
+ })
+}
+
+// 删除定时任务调度
+export function delJob(jobId) {
+ return request({
+ url: '/schedule/job/' + jobId,
+ method: 'delete'
+ })
+}
+
+// 任务状态修改
+export function changeJobStatus(jobId, status) {
+ const data = {
+ jobId,
+ status
+ }
+ return request({
+ url: '/schedule/job/changeStatus',
+ method: 'put',
+ data: data
+ })
+}
+
+
+// 定时任务立即执行一次
+export function runJob(jobId, jobGroup) {
+ const data = {
+ jobId,
+ jobGroup
+ }
+ return request({
+ url: '/schedule/job/run',
+ method: 'put',
+ data: data
+ })
+}
\ No newline at end of file
diff --git a/ruoyi-ui/src/api/monitor/jobLog.js b/ruoyi-ui/src/api/monitor/jobLog.js
new file mode 100644
index 0000000..732cc05
--- /dev/null
+++ b/ruoyi-ui/src/api/monitor/jobLog.js
@@ -0,0 +1,26 @@
+import request from '@/utils/request'
+
+// 查询调度日志列表
+export function listJobLog(query) {
+ return request({
+ url: '/schedule/job/log/list',
+ method: 'get',
+ params: query
+ })
+}
+
+// 删除调度日志
+export function delJobLog(jobLogId) {
+ return request({
+ url: '/schedule/job/log/' + jobLogId,
+ method: 'delete'
+ })
+}
+
+// 清空调度日志
+export function cleanJobLog() {
+ return request({
+ url: '/schedule/job/log/clean',
+ method: 'delete'
+ })
+}
diff --git a/ruoyi-ui/src/api/monitor/online.js b/ruoyi-ui/src/api/monitor/online.js
new file mode 100644
index 0000000..288ebe0
--- /dev/null
+++ b/ruoyi-ui/src/api/monitor/online.js
@@ -0,0 +1,18 @@
+import request from '@/utils/request'
+
+// 查询在线用户列表
+export function list(query) {
+ return request({
+ url: '/monitor/online/list',
+ method: 'get',
+ params: query
+ })
+}
+
+// 强退用户
+export function forceLogout(tokenId) {
+ return request({
+ url: '/monitor/online/' + tokenId,
+ method: 'delete'
+ })
+}
diff --git a/ruoyi-ui/src/api/system/config.js b/ruoyi-ui/src/api/system/config.js
new file mode 100644
index 0000000..ba38d1c
--- /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'
+ })
+}
diff --git a/ruoyi-ui/src/api/system/dept.js b/ruoyi-ui/src/api/system/dept.js
new file mode 100644
index 0000000..0535e8a
--- /dev/null
+++ b/ruoyi-ui/src/api/system/dept.js
@@ -0,0 +1,68 @@
+import request from '@/utils/request'
+
+// 查询部门列表
+export function listDept(query) {
+ return request({
+ url: '/system/dept/list',
+ method: 'get',
+ params: query
+ })
+}
+
+// 查询部门列表(排除节点)
+export function listDeptExcludeChild(deptId) {
+ return request({
+ url: '/system/dept/list/exclude/' + deptId,
+ method: 'get'
+ })
+}
+
+// 查询部门详细
+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..ca10f2f
--- /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'
+ })
+}
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..aeb4a1d
--- /dev/null
+++ b/ruoyi-ui/src/api/system/dict/type.js
@@ -0,0 +1,52 @@
+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'
+ })
+}
+
+// 获取字典选择框列表
+export function optionselect() {
+ return request({
+ url: '/system/dict/type/optionselect',
+ method: 'get'
+ })
+}
\ No newline at end of file
diff --git a/ruoyi-ui/src/api/system/logininfor.js b/ruoyi-ui/src/api/system/logininfor.js
new file mode 100644
index 0000000..8f21569
--- /dev/null
+++ b/ruoyi-ui/src/api/system/logininfor.js
@@ -0,0 +1,26 @@
+import request from '@/utils/request'
+
+// 查询登录日志列表
+export function list(query) {
+ return request({
+ url: '/system/logininfor/list',
+ method: 'get',
+ params: query
+ })
+}
+
+// 删除登录日志
+export function delLogininfor(infoId) {
+ return request({
+ url: '/system/logininfor/' + infoId,
+ method: 'delete'
+ })
+}
+
+// 清空登录日志
+export function cleanLogininfor() {
+ return request({
+ url: '/system/logininfor/clean',
+ 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/operlog.js b/ruoyi-ui/src/api/system/operlog.js
new file mode 100644
index 0000000..28a85d2
--- /dev/null
+++ b/ruoyi-ui/src/api/system/operlog.js
@@ -0,0 +1,26 @@
+import request from '@/utils/request'
+
+// 查询操作日志列表
+export function list(query) {
+ return request({
+ url: '/system/operlog/list',
+ method: 'get',
+ params: query
+ })
+}
+
+// 删除操作日志
+export function delOperlog(operId) {
+ return request({
+ url: '/monitor/operlog/' + operId,
+ method: 'delete'
+ })
+}
+
+// 清空操作日志
+export function cleanOperlog() {
+ return request({
+ url: '/system/operlog/clean',
+ method: 'delete'
+ })
+}
diff --git a/ruoyi-ui/src/api/system/post.js b/ruoyi-ui/src/api/system/post.js
new file mode 100644
index 0000000..8faa266
--- /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'
+ })
+}
diff --git a/ruoyi-ui/src/api/system/role.js b/ruoyi-ui/src/api/system/role.js
new file mode 100644
index 0000000..adad862
--- /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'
+ })
+}
diff --git a/ruoyi-ui/src/api/system/user.js b/ruoyi-ui/src/api/system/user.js
new file mode 100644
index 0000000..559f392
--- /dev/null
+++ b/ruoyi-ui/src/api/system/user.js
@@ -0,0 +1,110 @@
+import request from '@/utils/request'
+import { praseStrEmpty } from "@/utils/ruoyi";
+
+// 查询用户列表
+export function listUser(query) {
+ return request({
+ url: '/system/user/list',
+ method: 'get',
+ params: query
+ })
+}
+
+// 查询用户详细
+export function getUser(userId) {
+ return request({
+ url: '/system/user/' + praseStrEmpty(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
+ })
+}
diff --git a/ruoyi-ui/src/api/tool/gen.js b/ruoyi-ui/src/api/tool/gen.js
new file mode 100644
index 0000000..6ea27d1
--- /dev/null
+++ b/ruoyi-ui/src/api/tool/gen.js
@@ -0,0 +1,59 @@
+import request from '@/utils/request'
+
+// 查询生成表数据
+export function listTable(query) {
+ return request({
+ url: '/code/gen/list',
+ method: 'get',
+ params: query
+ })
+}
+// 查询db数据库列表
+export function listDbTable(query) {
+ return request({
+ url: '/code/gen/db/list',
+ method: 'get',
+ params: query
+ })
+}
+
+// 查询表详细信息
+export function getGenTable(tableId) {
+ return request({
+ url: '/code/gen/' + tableId,
+ method: 'get'
+ })
+}
+
+// 修改代码生成信息
+export function updateGenTable(data) {
+ return request({
+ url: '/code/gen',
+ method: 'put',
+ data: data
+ })
+}
+
+// 导入表
+export function importTable(data) {
+ return request({
+ url: '/code/gen/importTable',
+ method: 'post',
+ params: data
+ })
+}
+// 预览生成代码
+export function previewTable(tableId) {
+ return request({
+ url: '/code/gen/preview/' + tableId,
+ method: 'get'
+ })
+}
+// 删除表数据
+export function delTable(tableId) {
+ return request({
+ url: '/code/gen/' + tableId,
+ method: 'delete'
+ })
+}
+
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/cascader.svg b/ruoyi-ui/src/assets/icons/svg/cascader.svg
new file mode 100644
index 0000000..e256024
--- /dev/null
+++ b/ruoyi-ui/src/assets/icons/svg/cascader.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="1576153230908" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="971" xmlns:xlink="http://www.w3.org/1999/xlink" width="81" height="81"><defs><style type="text/css"></style></defs><path d="M772.87036133 734.06115723c-43.34106445 0-80.00793458 27.93273926-93.76831055 66.57714843H475.90991211c-56.60705567 0-102.66723633-46.06018067-102.66723633-102.66723633V600.82446289h305.859375c13.76037598 38.64440918 50.42724609 66.57714844 93.76831055 66.57714844 55.12390137 0 99.94812012-44.82421875 99.94812012-99.94812012S827.9942627 467.50537109 772.87036133 467.50537109c-43.34106445 0-80.00793458 27.93273926-93.76831055 66.57714844H373.24267578V401.01062011h321.92687989c55.12390137 0 99.94812012-44.82421875 99.94812011-99.94812011V190.07312011C795.11767578 134.94921875 750.29345703 90.125 695.16955567 90.125H251.12963867C196.0057373 90.125 151.18151855 134.94921875 151.18151855 190.07312011V301.0625c0 55.12390137 44.82421875 99.94812012 99.94812012 99.94812012h55.53588867v296.96044921c0 93.35632325 75.97045898 169.32678223 169.32678224 169.32678223h203.19213866c13.76037598 38.64440918 50.42724609 66.57714844 93.76831055 66.57714844 55.12390137 0 99.94812012-44.82421875 99.94812012-99.94812012s-44.90661622-99.86572266-100.03051758-99.86572265z m0-199.89624024c18.37463379 0 33.28857422 14.91394043 33.28857422 33.28857423s-14.91394043 33.28857422-33.28857422 33.28857421-33.28857422-14.91394043-33.28857422-33.28857421 14.91394043-33.28857422 33.28857422-33.28857422zM217.75866699 301.0625V190.07312011c0-18.37463379 14.91394043-33.28857422 33.28857423-33.28857421h444.03991698c18.37463379 0 33.28857422 14.91394043 33.28857422 33.28857422V301.0625c0 18.37463379-14.91394043 33.28857422-33.28857422 33.28857422H251.12963867c-18.37463379 0-33.37097168-14.91394043-33.37097168-33.28857422z m555.11169434 566.23535156c-18.37463379 0-33.28857422-14.91394043-33.28857422-33.28857422 0-18.37463379 14.91394043-33.28857422 33.28857422-33.28857422s33.28857422 14.91394043 33.28857422 33.28857422c0.08239747 18.29223633-14.91394043 33.28857422-33.28857422 33.28857422z" p-id="972"></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/checkbox.svg b/ruoyi-ui/src/assets/icons/svg/checkbox.svg
new file mode 100644
index 0000000..013fd3a
--- /dev/null
+++ b/ruoyi-ui/src/assets/icons/svg/checkbox.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="1575982282951" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="902" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M828.40625 90.125H195.59375C137.375 90.125 90.125 137.375 90.125 195.59375v632.8125c0 58.21875 47.25 105.46875 105.46875 105.46875h632.8125c58.21875 0 105.46875-47.25 105.46875-105.46875V195.59375c0-58.21875-47.25-105.46875-105.46875-105.46875z m52.734375 738.28125c0 29.16-23.57015625 52.734375-52.734375 52.734375H195.59375c-29.109375 0-52.734375-23.574375-52.734375-52.734375V195.59375c0-29.109375 23.625-52.734375 52.734375-52.734375h632.8125c29.16 0 52.734375 23.625 52.734375 52.734375v632.8125z" p-id="903"></path><path d="M421.52890625 709.55984375a36.28125 36.28125 0 0 1-27.55265625-12.66890625L205.17453125 476.613125a36.28546875 36.28546875 0 0 1 55.10109375-47.22890625l164.986875 192.4846875 342.16171875-298.48078125a36.2896875 36.2896875 0 0 1 47.70984375 54.68765625L445.3859375 700.6203125a36.3234375 36.3234375 0 0 1-23.85703125 8.93953125z" p-id="904"></path></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/color.svg b/ruoyi-ui/src/assets/icons/svg/color.svg
new file mode 100644
index 0000000..44a81aa
--- /dev/null
+++ b/ruoyi-ui/src/assets/icons/svg/color.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="1577252187056" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2508" xmlns:xlink="http://www.w3.org/1999/xlink" width="81" height="81"><defs><style type="text/css"></style></defs><path d="M747.59340925 691.12859384c11.51396329 0.25305413 22.43746719-0.21087818 40.74171707-1.51832482 29.35428085-2.10878421 35.84933734-2.36183835 46.47761114-0.8856895 24.71495444 3.37405491 41.12129828 21.76265671 32.47528161 47.95376084-85.57447632 258.19957947-442.00123984 249.76444099-628.67084683 50.73735554-153.47733892-159.33976008-153.09775772-414.41833795 0.92786545-573.42069196 159.71934128-162.67163983 424.03439521-166.59397897 565.78689185 0.63263534 80.38686649 94.81095318 108.34934958 169.16669549 89.11723508 230.57450162-15.01454608 47.99593598-50.61082928 77.68762207-119.77896259 114.63352789-4.89237973 2.65706845-29.35428085 15.52065436-35.84933652 19.02123633-46.94154346 25.30541465-63.51659033 41.20565021-62.20914449 58.45550757 2.95229856 39.13904114 24.16667102 52.7196135 70.98168823 53.81618115z m44.41100207 50.10472101c-19.82257471 1.43397372-32.05352527 1.940082-45.63409763 1.6448519-70.34905207-1.60267593-115.98314969-30.91478165-121.38163769-101.64341492-3.45840683-46.05585397 24.7571304-73.13264758 89.24376132-107.96976837 6.7902866-3.66928501 31.37871396-16.57504688 36.06021551-19.06341229 57.69634516-30.83042972 85.15271997-53.73183005 94.76877722-84.47790866 12.77923398-40.78389304-9.10994898-98.94417051-79.24812286-181.6507002-121.17075953-142.97559219-350.14258521-139.60153647-489.2380134 2.06660824-134.49827774 138.84237405-134.79350784 362.12048163-0.42175717 501.637667 158.53842169 168.99799328 451.9968783 181.18676788 534.57688175-11.80919339-4.68150156 0.2952301-10.71262573 0.67481131-18.72600705 1.26527069z" p-id="2509"></path><path d="M346.03865637 637.18588562a78.82636652 78.82636652 0 0 0 78.32025825-79.29029883c0-43.69401562-35.005823-79.29029883-78.32025825-79.29029882a78.82636652 78.82636652 0 0 0-78.36243338 79.29029882c0 43.69401562 35.005823 79.29029883 78.36243338 79.29029883z m0-51.7495729a27.07679361 27.07679361 0 0 1-26.5706845-27.54072593c0-15.30977536 11.97789643-27.54072593 26.5706845-27.54072592 14.55061295 0 26.57068533 12.23095057 26.57068533 27.54072592a27.07679361 27.07679361 0 0 1-26.57068533 27.54072593zM475.7289063 807.11174353a78.82636652 78.82636652 0 0 0 78.3624334-79.29029882c0-43.69401562-34.96364785-79.29029883-78.32025825-79.29029883a78.82636652 78.82636652 0 0 0-78.32025742 79.29029883c0 43.69401562 34.96364785 79.29029883 78.32025742 79.29029882z m0-51.74957208a27.07679361 27.07679361 0 0 1-26.57068532-27.54072674c0-15.30977536 12.06224753-27.54072593 26.57068532-27.54072593 14.59278892 0 26.57068533 12.23095057 26.57068453 27.54072593a27.07679361 27.07679361 0 0 1-26.57068453 27.54072674zM601.24376214 377.21492718a78.82636652 78.82636652 0 0 0 78.32025742-79.29029883c0-43.69401562-34.96364785-79.29029883-78.32025742-79.29029882a78.82636652 78.82636652 0 0 0-78.32025823 79.29029883c0 43.69401562 34.96364785 79.29029883 78.32025824 79.29029883z m1e-8-51.74957208a27.07679361 27.07679361 0 0 1-26.57068534-27.54072675c0-15.30977536 11.97789643-27.54072593 26.57068534-27.54072591 14.55061295 0 26.57068533 12.23095057 26.57068451 27.54072592a27.07679361 27.07679361 0 0 1-26.57068451 27.54072674zM378.80916809 433.85687983a78.82636652 78.82636652 0 0 0 78.32025824-79.29029883c0-43.69401562-34.96364785-79.29029883-78.32025824-79.29029802a78.82636652 78.82636652 0 0 0-78.32025742 79.29029802c0 43.69401562 34.96364785 79.29029883 78.32025742 79.29029883z m0-51.74957209a27.07679361 27.07679361 0 0 1-26.57068451-27.54072674c0-15.30977536 11.97789643-27.54072593 26.57068451-27.54072593 14.55061295 0 26.57068533 12.23095057 26.57068533 27.54072593a27.07679361 27.07679361 0 0 1-26.57068533 27.54072674z" p-id="2510"></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..29c3458
--- /dev/null
+++ b/ruoyi-ui/src/assets/icons/svg/component.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="1575804206892" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3145" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M826.56 470.016c-32.896 0-64.384 12.288-89.984 35.52l0-104.96c0-62.208-50.496-112.832-112.64-113.088L623.936 287.04 519.552 287.104C541.824 262.72 554.56 230.72 554.56 197.12c0-73.536-59.904-133.44-133.504-133.44-73.472 0-133.376 59.904-133.376 133.44 0 32.896 12.224 64.256 35.52 89.984L175.232 287.104l0 0.576C113.728 288.704 64 338.88 64 400.576l0.32 0 0.32 116.48C60.864 544.896 70.592 577.728 100.8 588.48c12.736 4.608 37.632 7.488 60.864-25.28 12.992-18.368 34.24-29.248 56.64-29.248 38.336 0 69.504 31.104 69.504 69.312 0 38.4-31.168 69.504-69.504 69.504-22.656 0-44.032-11.264-57.344-30.4C138.688 610.112 112.576 615.36 102.464 619.136c-29.824 10.752-39.104 43.776-38.144 67.392l0 160.384L64 846.912C64 909.248 114.752 960 177.216 960l446.272 0c62.4 0 113.152-50.752 113.152-113.152l0-145.024c24.384 22.272 56.384 35.008 89.984 35.008 73.536 0 133.44-59.904 133.44-133.504C960 529.92 900.096 470.016 826.56 470.016zM826.56 672.896c-22.72 0-44.032-11.264-57.344-30.4-22.272-32.384-48.448-27.136-58.56-23.36-29.824 10.752-39.04 43.776-38.08 67.392l0 160.384c0 27.136-22.016 49.152-49.152 49.152L177.216 896.064C150.08 896 128 873.984 128 846.848l0.32 0 0-145.024c24.384 22.272 56.384 35.008 89.984 35.008 73.6 0 133.504-59.904 133.504-133.504 0-73.472-59.904-133.376-133.504-133.376-32.896 0-64.32 12.288-89.984 35.52l0-104.96L128 400.512c0-27.072 22.08-49.152 49.216-49.152L177.216 351.04 334.656 350.72c3.776 0.512 7.616 0.832 11.52 0.832 24.896 0 50.752-10.816 60.032-37.056 4.544-12.736 7.424-37.568-25.344-60.736C362.624 240.768 351.68 219.52 351.68 197.12c0-38.272 31.104-69.44 69.376-69.44 38.336 0 69.504 31.168 69.504 69.44 0 22.72-11.264 44.032-30.528 57.472C427.968 276.736 433.088 302.784 436.8 313.024c10.752 29.888 43.072 39.232 67.392 38.08l119.232 0 0 0.384c27.136 0 49.152 22.08 49.152 49.152l0.256 116.48c-3.776 27.84 6.016 60.736 36.224 71.488 12.736 4.608 37.632 7.488 60.8-25.28 13.056-18.368 34.24-29.248 56.704-29.248C864.832 534.016 896 565.12 896 603.392 896 641.728 864.832 672.896 826.56 672.896z" p-id="3146"></path></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-range.svg b/ruoyi-ui/src/assets/icons/svg/date-range.svg
new file mode 100644
index 0000000..fda571e
--- /dev/null
+++ b/ruoyi-ui/src/assets/icons/svg/date-range.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="1579774833889" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1376" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M887.466667 192.853333h-100.693334V119.466667c0-10.24-6.826667-17.066667-17.066666-17.066667s-17.066667 6.826667-17.066667 17.066667v73.386666H303.786667V119.466667c0-10.24-6.826667-17.066667-17.066667-17.066667s-17.066667 6.826667-17.066667 17.066667v73.386666H168.96c-46.08 0-85.333333 37.546667-85.333333 85.333334V836.266667c0 46.08 37.546667 85.333333 85.333333 85.333333H887.466667c46.08 0 85.333333-37.546667 85.333333-85.333333V278.186667c0-47.786667-37.546667-85.333333-85.333333-85.333334z m-718.506667 34.133334h100.693333v66.56c0 10.24 6.826667 17.066667 17.066667 17.066666s17.066667-6.826667 17.066667-17.066666v-66.56h450.56v66.56c0 10.24 6.826667 17.066667 17.066666 17.066666s17.066667-6.826667 17.066667-17.066666v-66.56H887.466667c27.306667 0 51.2 22.186667 51.2 51.2v88.746666H117.76v-88.746666c0-29.013333 22.186667-51.2 51.2-51.2zM887.466667 887.466667H168.96c-27.306667 0-51.2-22.186667-51.2-51.2V401.066667H938.666667V836.266667c0 27.306667-22.186667 51.2-51.2 51.2z" p-id="1377"></path><path d="M858.453333 493.226667H327.68c-10.24 0-17.066667 6.826667-17.066667 17.066666v114.346667h-116.053333c-10.24 0-17.066667 6.826667-17.066667 17.066667v133.12c0 10.24 6.826667 17.066667 17.066667 17.066666H460.8c10.24 0 17.066667-6.826667 17.066667-17.066666v-114.346667h380.586666c10.24 0 17.066667-6.826667 17.066667-17.066667v-133.12c0-10.24-6.826667-17.066667-17.066667-17.066666z m-413.013333 34.133333v97.28h-98.986667v-97.28h98.986667z m-230.4 131.413333h98.986667v98.986667h-98.986667v-98.986667z m131.413333 97.28v-97.28h98.986667v97.28h-98.986667z m133.12-228.693333h97.28v98.986667h-97.28v-98.986667z m131.413334 0h98.986666v98.986667h-98.986666v-98.986667z m230.4 97.28h-98.986667v-98.986667h98.986667v98.986667z" p-id="1378"></path></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..52dc73e
--- /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="1577186573535" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1068" xmlns:xlink="http://www.w3.org/1999/xlink" width="81" height="81"><defs><style type="text/css"></style></defs><path d="M479.85714249 608.42857168h64.28571502c19.28571417 0 32.14285751-12.85714249 32.14285664-32.14285751s-12.85714249-32.14285751-32.14285664-32.14285664h-64.28571504c-19.28571417 0-32.14285751 12.85714249-32.14285664 32.14285662s12.85714249 32.14285751 32.14285664 32.14285753z m-2e-8 122.14285665h64.28571504c19.28571417 0 32.14285751-12.85714249 32.14285664-32.14285665s-12.85714249-32.14285751-32.14285664-32.14285751h-64.28571504c-19.28571417 0-32.14285751 12.85714249-32.14285664 32.14285751s12.85714249 32.14285751 32.14285664 32.14285664z m353.57142921-559.28571416h-128.57142921v-32.14285664c0-19.28571417-12.85714249-32.14285751-32.14285664-32.14285753s-32.14285751 12.85714249-32.14285751 32.14285753v32.14285664h-257.14285665v-32.14285664c0-19.28571417-12.85714249-32.14285751-32.14285752-32.14285753s-32.14285751 12.85714249-32.14285664 32.14285753v32.14285664h-128.57142919c-70.71428585 0-128.57142832 57.85714249-128.57142832 122.14285751v501.42857081c0 70.71428585 57.85714249 128.57142832 128.57142832 122.14285751h642.85714335c70.71428585 0 128.57142832-57.85714249 128.57142833-122.14285751v-501.42857081c0-70.71428585-57.85714249-122.14285753-128.57142833-122.14285751z m64.28571415 623.57142832c0 32.14285751-32.14285751 64.28571415-64.28571416 64.28571504h-642.85714335c-32.14285751 0-64.28571415-25.71428583-64.28571417-64.28571504v-372.85714249h771.42857168v372.85714249z m0-437.14285664h-771.42857168v-64.28571417c0-32.14285751 32.14285751-64.28571415 64.28571417-64.28571415h128.57142919v32.14285664c0 19.28571417 12.85714249 32.14285751 32.14285664 32.14285751s32.14285751-12.85714249 32.14285753-32.14285751v-32.14285664h257.14285665v32.14285664c0 19.28571417 12.85714249 32.14285751 32.1428575 32.14285751s32.14285751-12.85714249 32.14285664-32.14285751v-32.14285664h128.57142921c32.14285751 0 64.28571415 25.71428583 64.28571415 64.28571415v64.28571417z m-610.71428583 372.85714247h64.28571415c19.28571417 0 32.14285751-12.85714249 32.14285753-32.14285664s-12.85714249-32.14285751-32.14285753-32.14285751h-64.28571415c-19.28571417 0-32.14285751 12.85714249-32.14285751 32.14285751s12.85714249 32.14285751 32.14285751 32.14285665z m385.71428583-122.14285664h64.28571417c19.28571417 0 32.14285751-12.85714249 32.14285751-32.14285751s-12.85714249-32.14285751-32.14285751-32.14285664h-64.28571415c-19.28571417 0-32.14285751 12.85714249-32.14285753 32.14285664s12.85714249 32.14285751 32.14285753 32.14285751z m-385.71428583 0h64.28571415c19.28571417 0 32.14285751-12.85714249 32.14285753-32.14285751s-12.85714249-32.14285751-32.14285753-32.14285664h-64.28571415c-19.28571417 0-32.14285751 12.85714249-32.14285751 32.14285664s12.85714249 32.14285751 32.14285751 32.14285751z m385.71428583 122.14285665h64.28571417c19.28571417 0 32.14285751-12.85714249 32.14285751-32.14285665s-12.85714249-32.14285751-32.14285751-32.14285751h-64.28571415c-19.28571417 0-32.14285751 12.85714249-32.14285753 32.14285751s12.85714249 32.14285751 32.14285753 32.14285665z" p-id="1069"></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/github.svg b/ruoyi-ui/src/assets/icons/svg/github.svg
new file mode 100644
index 0000000..db0a0d4
--- /dev/null
+++ b/ruoyi-ui/src/assets/icons/svg/github.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="1581238998885" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4187" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M511.542857 14.057143C228.914286 13.942857 0 242.742857 0 525.142857 0 748.457143 143.2 938.285714 342.628571 1008c26.857143 6.742857 22.742857-12.342857 22.742858-25.371429v-88.571428c-155.085714 18.171429-161.371429-84.457143-171.771429-101.6C172.571429 756.571429 122.857143 747.428571 137.714286 730.285714c35.314286-18.171429 71.314286 4.571429 113.028571 66.171429 30.171429 44.685714 89.028571 37.142857 118.857143 29.714286 6.514286-26.857143 20.457143-50.857143 39.657143-69.485715-160.685714-28.8-227.657143-126.857143-227.657143-243.428571 0-56.571429 18.628571-108.571429 55.2-150.514286-23.314286-69.142857 2.171429-128.342857 5.6-137.142857 66.4-5.942857 135.428571 47.542857 140.8 51.771429 37.714286-10.171429 80.8-15.542857 129.028571-15.542858 48.457143 0 91.657143 5.6 129.714286 15.885715 12.914286-9.828571 76.914286-55.771429 138.628572-50.171429 3.314286 8.8 28.228571 66.628571 6.285714 134.857143 37.028571 42.057143 55.885714 94.514286 55.885714 151.2 0 116.8-67.428571 214.971429-228.571428 243.314286a145.714286 145.714286 0 0 1 43.542857 104v128.571428c0.914286 10.285714 0 20.457143 17.142857 20.457143 202.4-68.228571 348.114286-259.428571 348.114286-484.685714 0-282.514286-229.028571-511.2-511.428572-511.2z" p-id="4188"></path></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/input.svg b/ruoyi-ui/src/assets/icons/svg/input.svg
new file mode 100644
index 0000000..ab91381
--- /dev/null
+++ b/ruoyi-ui/src/assets/icons/svg/input.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="1575802859706" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3102" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M896 224H128c-35.2 0-64 28.8-64 64v448c0 35.2 28.8 64 64 64h768c35.2 0 64-28.8 64-64V288c0-35.2-28.8-64-64-64z m0 480c0 19.2-12.8 32-32 32H160c-19.2 0-32-12.8-32-32V320c0-19.2 12.8-32 32-32h704c19.2 0 32 12.8 32 32v384z" p-id="3103"></path><path d="M224 352c-19.2 0-32 12.8-32 32v256c0 16 12.8 32 32 32s32-12.8 32-32V384c0-16-12.8-32-32-32z" p-id="3104"></path></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/number.svg b/ruoyi-ui/src/assets/icons/svg/number.svg
new file mode 100644
index 0000000..ad5ce9a
--- /dev/null
+++ b/ruoyi-ui/src/assets/icons/svg/number.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="1575802851180" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2867" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M279.272727 791.272727h512a46.545455 46.545455 0 0 1 0 93.090909H279.272727a46.545455 46.545455 0 0 1 0-93.090909z m33.838546-617.984V651.636364H193.722182V395.170909c0-37.003636-0.884364-59.298909-2.653091-66.746182a24.948364 24.948364 0 0 0-14.615273-16.989091c-8.005818-3.863273-25.786182-5.771636-53.341091-5.771636h-11.822545v-55.854545c57.716364-12.381091 101.562182-37.888 131.490909-76.520728h70.283636z m303.709091 396.8V651.636364H354.164364v-68.235637c77.777455-127.255273 124.043636-206.010182 138.705454-236.218182 14.661818-30.254545 22.016-53.853091 22.016-70.74909 0-13.032727-2.234182-22.714182-6.656-29.137455-4.421818-6.376727-11.170909-9.588364-20.247273-9.588364a22.248727 22.248727 0 0 0-20.200727 10.612364c-4.468364 7.121455-6.656 21.178182-6.656 42.263273v45.521454H354.164364v-17.454545c0-26.763636 1.396364-47.941818 4.142545-63.348364 2.746182-15.499636 9.541818-30.72 20.386909-45.661091 10.798545-14.987636 24.901818-26.298182 42.216727-33.978182 17.361455-7.68 38.167273-11.543273 62.37091-11.543272 47.476364 0 83.316364 11.776 107.706181 35.328 24.296727 23.552 36.445091 53.341091 36.445091 89.367272 0 27.368727-6.842182 56.32-20.48 86.853819-13.730909 30.533818-54.039273 95.325091-121.018182 194.420363h130.885819z m270.615272-189.393454c18.152727 6.097455 31.650909 16.104727 40.494546 29.975272 8.843636 13.917091 13.312 46.452364 13.312 97.652364 0 38.027636-4.328727 67.490909-13.032727 88.529455-8.657455 20.945455-23.598545 36.910545-44.869819 47.848727-21.271273 10.938182-48.593455 16.384-81.873454 16.384-37.794909 0-67.490909-6.330182-89.088-19.083636-21.550545-12.660364-35.746909-28.253091-42.542546-46.638546-6.795636-18.432-10.193455-50.362182-10.193454-95.883636v-37.841455h119.389091v77.730909c0 20.666182 1.210182 33.838545 3.723636 39.424 2.420364 5.585455 7.912727 8.424727 16.337455 8.424728 9.309091 0 15.36-3.537455 18.338909-10.612364 2.932364-7.121455 4.421818-25.6 4.421818-55.575273v-33.047273c0-18.338909-2.048-31.744-6.190546-40.215272a30.72 30.72 0 0 0-18.338909-16.709818c-8.052364-2.653091-23.738182-4.189091-46.964363-4.561455V357.050182c28.392727 0 45.893818-1.070545 52.596363-3.258182a22.946909 22.946909 0 0 0 14.475637-14.149818c2.932364-7.307636 4.421818-18.711273 4.421818-34.257455v-26.624c0-16.756364-1.722182-27.741091-5.12-33.047272-3.490909-5.352727-8.843636-8.005818-16.151273-8.005819-8.285091 0-13.963636 2.792727-16.989091 8.378182-3.025455 5.632-4.561455 17.640727-4.561454 35.933091v39.284364h-119.389091v-40.773818c0-45.661091 10.472727-76.567273 31.325091-92.625455 20.898909-16.058182 54.085818-24.064 99.607272-24.064 56.878545 0 95.511273 11.170909 115.805091 33.373091 20.293818 22.248727 30.394182 53.201455 30.394182 92.765091 0 26.810182-3.630545 46.173091-10.891636 58.088727-7.307636 11.915636-20.107636 22.807273-38.446546 32.628364z" p-id="2868"></path></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..6c64def
--- /dev/null
+++ b/ruoyi-ui/src/assets/icons/svg/password.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="1575802846045" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2750" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M868.593046 403.832442c-30.081109-28.844955-70.037123-44.753273-112.624057-44.753273L265.949606 359.079168c-42.554188 0-82.510202 15.908318-112.469538 44.690852-30.236652 28.782533-46.857191 67.222007-46.857191 108.198258l0 294.079782c0 40.977273 16.619516 79.414701 46.702672 108.136859 29.959336 28.844955 70.069869 44.814672 112.624057 44.814672l490.019383 0c42.585911 0 82.696444-15.969717 112.624057-44.814672 30.082132-28.844955 46.579875-67.222007 46.579875-108.136859L915.172921 511.968278C915.171897 471.053426 898.675178 432.677397 868.593046 403.832442zM841.821309 806.049083c0 22.098297-8.882298 42.772152-25.099654 58.306964-16.154935 15.661701-37.81935 24.203238-60.752666 24.203238L265.949606 888.559285c-22.934339 0-44.567032-8.54256-60.877509-24.264637-16.186657-15.474436-25.067932-36.148291-25.067932-58.246589L180.004165 511.968278c0-22.035876 8.881274-42.772152 25.192775-58.307987 16.186657-15.536858 37.81935-24.139793 60.753689-24.139793l490.019383 0c22.933315 0 44.597731 8.602935 60.752666 24.139793 16.21838 15.535835 25.099654 36.272112 25.099654 58.307987L841.822332 806.049083zM510.974136 135.440715c114.914216 0 208.318536 89.75214 208.318536 200.055338l73.350588 0c0-149.113109-126.366036-270.496667-281.669124-270.496667-155.333788 0-281.699824 121.383558-281.699824 270.496667l73.350588 0C302.623877 225.193879 396.059919 135.440715 510.974136 135.440715zM474.299865 747.244792l73.350588 0L547.650453 629.576859l-73.350588 0L474.299865 747.244792z" p-id="2751"></path></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/question.svg b/ruoyi-ui/src/assets/icons/svg/question.svg
new file mode 100644
index 0000000..cf75bd4
--- /dev/null
+++ b/ruoyi-ui/src/assets/icons/svg/question.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="1581238842264" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1409" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M512 0C229.233778 0 0 229.233778 0 512s229.233778 512 512 512 512-229.233778 512-512A512 512 0 0 0 512 0z m0 938.666667C276.366222 938.666667 85.333333 747.633778 85.333333 512 85.333333 276.366222 276.366222 85.333333 512 85.333333c235.633778 0 426.666667 191.032889 426.666667 426.666667a426.666667 426.666667 0 0 1-426.666667 426.666667z m0-717.653334a170.666667 170.666667 0 0 0-170.666667 170.666667 42.666667 42.666667 0 0 0 85.333334 0 85.333333 85.333333 0 1 1 85.333333 85.333333 42.666667 42.666667 0 0 0-42.666667 42.666667v111.36a42.666667 42.666667 0 0 0 85.333334 0v-74.24A170.666667 170.666667 0 0 0 512 221.013333z m-42.666667 542.293334a42.666667 42.666667 0 1 0 85.333334 0 42.666667 42.666667 0 0 0-85.333334 0z" p-id="1410"></path></svg>
\ No newline at end of file
diff --git a/ruoyi-ui/src/assets/icons/svg/radio.svg b/ruoyi-ui/src/assets/icons/svg/radio.svg
new file mode 100644
index 0000000..0cde345
--- /dev/null
+++ b/ruoyi-ui/src/assets/icons/svg/radio.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="1575966775973" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="879" xmlns:xlink="http://www.w3.org/1999/xlink" width="81" height="81"><defs><style type="text/css"></style></defs><path d="M507.39346659 71.84873358c241.53533667 0 437.39770766 195.85422109 437.39770767 437.37442191 0 241.53766571-195.86237099 437.38955776-437.39770767 437.38955776-241.50040803 0-437.34997219-195.85189205-437.34997219-437.38955776C70.0434944 267.70295467 265.89189347 71.84873358 507.39346659 71.84873358L507.39346659 71.84873358zM507.39346659 282.81899805c-125.00686734 0-226.37039389 101.38914133-226.37039388 226.41813048 0 125.01268821 101.36352768 226.39717262 226.37039388 226.39717262 125.04295993 0 226.42395136-101.38448441 226.42395136-226.39717262C733.81625401 384.20813938 632.43642653 282.81899805 507.39346659 282.81899805L507.39346659 282.81899805zM507.39346659 120.78172615c-214.46664192 0-388.42047261 173.95150279-388.4204726 388.44026539 0 214.51204949 173.95499463 388.46122325 388.4204726 388.46122325 214.52369237 0 388.46005817-173.94800981 388.46005818-388.46122325C895.85236082 294.73322894 721.91715897 120.78172615 507.39346659 120.78172615z" p-id="880"></path></svg>
\ No newline at end of file
diff --git a/ruoyi-ui/src/assets/icons/svg/rate.svg b/ruoyi-ui/src/assets/icons/svg/rate.svg
new file mode 100644
index 0000000..aa3b14d
--- /dev/null
+++ b/ruoyi-ui/src/assets/icons/svg/rate.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="1577246781606" class="icon" viewBox="0 0 1069 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1098" xmlns:xlink="http://www.w3.org/1999/xlink" width="84.5595703125" height="81"><defs><style type="text/css"></style></defs><path d="M633.72929961 378.02038203l9.49872568 18.68789795 20.78025469 2.79745225 206.61592412 27.33248408a11.46496817 11.46496817 0 0 1 6.6095543 19.47324902l-147.2675168 147.35350284-14.89299345 14.89299345 3.8006376 20.68280244 37.84585956 204.89044571a11.46496817 11.46496817 0 0 1-16.4808914 12.2961788L554.68980898 751.84713388l-18.68789794-9.49299345-18.48726123 9.99171915-183.23885392 99.34968163a11.46496817 11.46496817 0 0 1-16.78471347-11.8662416l32.5433127-205.79617881 3.29617793-20.78598692-15.19108243-14.49172002-151.03375839-143.48407587a11.46496817 11.46496817 0 0 1 6.09936328-19.63949062l205.79617881-32.63503185 20.78598691-3.2961788L428.87898125 380.72038203 518.59235674 192.64331182a11.46496817 11.46496817 0 0 1 20.56815264-0.26369385l94.56879023 185.63503183zM496.64840732 85.52038203l-121.75796162 254.98089229L95.76433145 384.76178369A34.3949045 34.3949045 0 0 0 77.46050938 443.66879023l204.87324901 194.66369385-44.16879023 279.1146498a34.3949045 34.3949045 0 0 0 50.36560489 35.61592325l248.4-134.67898038 251.84522285 128.27579591a34.3949045 34.3949045 0 0 0 49.43694287-36.89426777l-51.30573223-277.85350284 199.73120977-199.90891758a34.3949045 34.3949045 0 0 0-19.82866201-58.40827998l-280.11783428-37.03184736L558.32993633 84.71210205a34.3949045 34.3949045 0 0 0-61.68152901 0.80254775z" p-id="1099"></path></svg>
\ No newline at end of file
diff --git a/ruoyi-ui/src/assets/icons/svg/row.svg b/ruoyi-ui/src/assets/icons/svg/row.svg
new file mode 100644
index 0000000..0780992
--- /dev/null
+++ b/ruoyi-ui/src/assets/icons/svg/row.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="1579339929870" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1182" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M152 854.856875h325.7146875V237.715625H134.856875v600q0 6.99375 5.0746875 12.0684375T152 854.856875z m737.143125-17.1421875v-600H546.284375v617.1421875H872q6.99375 0 12.0684375-5.07375t5.0746875-12.0684375z m68.5715625-651.429375V837.715625q0 35.3821875-25.16625 60.5484375T872 923.4284375H152q-35.383125 0-60.5484375-25.1653125T66.284375 837.7146875V186.284375q0-35.3821875 25.16625-60.5484375T152 100.5715625h720q35.383125 0 60.5484375 25.1653125t25.16625 60.5484375z" p-id="1183"></path></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/select.svg b/ruoyi-ui/src/assets/icons/svg/select.svg
new file mode 100644
index 0000000..d628382
--- /dev/null
+++ b/ruoyi-ui/src/assets/icons/svg/select.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="1575803481213" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="804" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M62 511.97954521C62 263.86590869 263.90681826 62 511.97954521 62s449.97954521 201.825 449.97954521 449.97954521c0 248.19545479-201.90681826 449.97954521-449.97954521 449.97954521C263.90681826 962 62 760.175 62 511.97954521M901.98636348 511.97954521c0-215.24318174-175.00909131-390.41590869-390.00681827-390.41590869-215.03863652 0-389.96590869 175.17272695-389.96590868 390.41590869 0 215.28409131 175.00909131 390.45681826 389.96590868 390.45681826C727.01818174 902.47727305 901.98636348 727.30454521 901.98636348 511.97954521M264.17272695 430.28409131c0-5.76818174 2.12727305-11.51590869 6.64772696-15.87272696 8.71363652-8.75454521 22.88863652-8.75454521 31.725 0l209.4340913 208.22727305L721.45454521 414.53409131c8.75454521-8.71363652 22.97045479-8.71363652 31.90909132 0 8.71363652 8.75454521 8.71363652 22.88863652 0 31.60227304L511.97954521 685.74090869 270.71818174 446.01363653C266.27954521 441.77954521 264.17272695 436.05227305 264.17272695 430.28409131" p-id="805"></path></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/slider.svg b/ruoyi-ui/src/assets/icons/svg/slider.svg
new file mode 100644
index 0000000..fbe4f39
--- /dev/null
+++ b/ruoyi-ui/src/assets/icons/svg/slider.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="1577185310368" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1238" xmlns:xlink="http://www.w3.org/1999/xlink" width="81" height="81"><defs><style type="text/css"></style></defs><path d="M951.453125 476.84375H523.671875a131.8359375 131.8359375 0 0 0-254.1796875 0H72.546875v70.3125h196.9453125a131.8359375 131.8359375 0 0 0 254.1796875 0H951.453125z" p-id="1239"></path></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/switch.svg b/ruoyi-ui/src/assets/icons/svg/switch.svg
new file mode 100644
index 0000000..0ba61e3
--- /dev/null
+++ b/ruoyi-ui/src/assets/icons/svg/switch.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="1576042673958" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1110" xmlns:xlink="http://www.w3.org/1999/xlink" width="81" height="81"><defs><style type="text/css"></style></defs><path d="M692 792H332c-150 0-270-120-270-270s120-270 270-270h360c150 0 270 120 270 270 0 147-120 270-270 270zM332 312c-117 0-210 93-210 210s93 210 210 210h360c117 0 210-93 210-210s-93-210-210-210H332z" p-id="1111"></path><path d="M341 522m-150 0a150 150 0 1 0 300 0 150 150 0 1 0-300 0Z" p-id="1112"></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/textarea.svg b/ruoyi-ui/src/assets/icons/svg/textarea.svg
new file mode 100644
index 0000000..2709f29
--- /dev/null
+++ b/ruoyi-ui/src/assets/icons/svg/textarea.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="1575802855098" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2984" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M896 160H128c-35.2 0-64 28.8-64 64v576c0 35.2 28.8 64 64 64h768c35.2 0 64-28.8 64-64V224c0-35.2-28.8-64-64-64z m0 608c0 16-12.8 32-32 32H160c-19.2 0-32-12.8-32-32V256c0-16 12.8-32 32-32h704c19.2 0 32 12.8 32 32v512z" p-id="2985"></path><path d="M224 288c-19.2 0-32 12.8-32 32v256c0 16 12.8 32 32 32s32-12.8 32-32V320c0-16-12.8-32-32-32z m608 480c19.2 0 32-12.8 32-32V608L704 768h128z" p-id="2986"></path></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/time-range.svg b/ruoyi-ui/src/assets/icons/svg/time-range.svg
new file mode 100644
index 0000000..13c1202
--- /dev/null
+++ b/ruoyi-ui/src/assets/icons/svg/time-range.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="1579774825624" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1248" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M498.595712 482.290351 345.420077 482.290351l0 57.307194 210.477712 0L555.897789 274.196942l-57.301054 0L498.596735 482.290351zM498.595712 482.290351" p-id="1249"></path><path d="M577.685002 644.98478l379.879913 0 0 57.302077L577.685002 702.286858 577.685002 644.98478 577.685002 644.98478zM577.685002 644.98478" p-id="1250"></path><path d="M577.685002 773.764795l379.879913 0 0 57.307194L577.685002 831.071989 577.685002 773.764795 577.685002 773.764795zM577.685002 773.764795" p-id="1251"></path><path d="M577.685002 902.549927l379.879913 0 0 57.307194L577.685002 959.857121 577.685002 902.549927 577.685002 902.549927zM577.685002 902.549927" p-id="1252"></path><path d="M102.523001 382.290823c4.450359 2.615571 9.470699 3.954055 14.530948 3.954055 2.969635 0 5.952572-0.461511 8.836249-1.394766l190.809767-61.886489c15.052834-4.882194 23.297612-21.040199 18.415418-36.08894-4.882194-15.052834-21.040199-23.297612-36.093033-18.415418L175.676092 308.458257c15.994276-26.115797 35.170011-50.537 57.370639-72.743768 73.767074-73.767074 171.845857-114.388237 276.16783-114.388237 104.32095 0 202.39564 40.622186 276.16169 114.388237s114.393353 171.845857 114.393353 276.16783c0 26.427906-2.615571 52.449559-7.709589 77.780481l58.302871 0c4.464685-25.499767 6.708795-51.470255 6.708795-77.780481 0-60.449767-11.845793-119.102608-35.204803-174.336584-22.559808-53.334719-54.850236-101.226472-95.968725-142.349055-41.122583-41.122583-89.017406-73.408917-142.348032-95.968725C628.317169 75.866898 569.659211 64.021106 509.215584 64.021106c-60.448744 0-119.106702 11.845793-174.336584 35.207873-53.334719 22.559808-101.230566 54.846142-142.349055 95.968725-23.980157 23.980157-44.934398 50.278103-62.727647 78.601172l-20.738323-105.655342c-3.043313-15.527648-18.105357-25.642007-33.631982-22.599717-15.527648 3.048429-25.64303 18.105357-22.599717 33.637098l36.102243 183.932126C90.51348 371.153158 95.460142 378.13313 102.523001 382.290823L102.523001 382.290823zM102.523001 382.290823" p-id="1253"></path><path d="M126.020158 587.9416 67.768453 587.9416c5.759167 33.679054 15.368012 66.544579 28.789697 98.278327 22.559808 53.333696 54.850236 101.225449 95.971795 142.348032 41.122583 41.122583 89.014336 73.408917 142.349055 95.968725 54.112432 22.88829 111.517863 34.71157 170.668031 35.18229L505.547031 902.395408c-102.94972-0.941442-199.594851-41.445948-272.499277-114.349351C177.545672 732.543975 140.810003 663.275355 126.020158 587.9416L126.020158 587.9416zM126.020158 587.9416" p-id="1254"></path></svg>
\ No newline at end of file
diff --git a/ruoyi-ui/src/assets/icons/svg/time.svg b/ruoyi-ui/src/assets/icons/svg/time.svg
new file mode 100644
index 0000000..b376e32
--- /dev/null
+++ b/ruoyi-ui/src/assets/icons/svg/time.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="1577099827399" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1008" xmlns:xlink="http://www.w3.org/1999/xlink" width="81" height="81"><defs><style type="text/css"></style></defs><path d="M520 559h204c17.673 0 32 14.327 32 32 0 17.673-14.327 32-32 32H488c-17.673 0-32-14.327-32-32 0-0.167 0.001-0.334 0.004-0.5a32.65 32.65 0 0 1-0.004-0.5V277c0-17.673 14.327-32 32-32 17.673 0 32 14.327 32 32v282z m-8 401C264.576 960 64 759.424 64 512S264.576 64 512 64s448 200.576 448 448-200.576 448-448 448z m0-64c212.077 0 384-171.923 384-384S724.077 128 512 128 128 299.923 128 512s171.923 384 384 384z" p-id="1009"></path></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/upload.svg b/ruoyi-ui/src/assets/icons/svg/upload.svg
new file mode 100644
index 0000000..bae49c0
--- /dev/null
+++ b/ruoyi-ui/src/assets/icons/svg/upload.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="1577540289643" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7922" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M530.944 458.24l4.8 3.456 122.176 106.816a32 32 0 0 1-37.44 51.584l-4.672-3.392L546.56 556.16v280.704a32 32 0 0 1-26.24 31.488l-5.76 0.512a32 32 0 0 1-31.424-26.24l-0.512-5.76-0.064-280.704-69.12 60.48a32 32 0 0 1-40.96 0.896l-4.16-3.968a32 32 0 0 1-0.96-40.96l4.032-4.16 122.176-106.816a32 32 0 0 1 37.312-3.456zM497.92 128c128.128 0 239.168 82.304 275.52 199.04 123.968 11.264 221.312 113.088 221.312 237.44 0 128.128-103.68 232.96-234.88 238.272h-5.888l-35.52 0.192a32 32 0 0 1-0.192-64l35.264-0.128 4.672-0.064c96.384-3.84 172.544-80.896 172.544-174.272 0-96.128-80.512-174.464-179.584-174.464h-1.984a32 32 0 0 1-32-25.28C695.872 264.96 604.736 192 497.92 192 381.824 192 285.44 277.76 274.816 388.48a32 32 0 0 1-28.352 28.8c-83.968 9.152-147.84 78.208-147.84 159.552l0.192 7.936c3.84 85.76 77.056 154.112 166.592 154.112h45.632a32 32 0 0 1 0 64h-45.632C142.016 802.944 40.32 708.032 34.88 586.88l-0.192-9.28c0-106.88 76.352-197.184 179.968-219.904C239.488 226.112 357.76 128 497.856 128z" p-id="7923"></path></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..121ffe2
--- /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..558eea4
--- /dev/null
+++ b/ruoyi-ui/src/assets/styles/element-ui.scss
@@ -0,0 +1,84 @@
+// 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;
+}
+
+// to fix el-date-picker css style
+.el-range-separator {
+ box-sizing: content-box;
+}
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..b6c40ac
--- /dev/null
+++ b/ruoyi-ui/src/assets/styles/ruoyi.scss
@@ -0,0 +1,197 @@
+ /**
+ * 通用css样式布局处理
+ * Copyright (c) 2019 ruoyi
+ */
+
+ /** 基础通用 **/
+.pt5 {
+ padding-top: 5px;
+}
+.pr5 {
+ padding-right: 5px;
+}
+.pb5 {
+ padding-bottom: 5px;
+}
+.mt5 {
+ margin-top: 5px;
+}
+.mr5 {
+ margin-right: 5px;
+}
+.mb5 {
+ margin-bottom: 5px;
+}
+.mb8 {
+ margin-bottom: 8px;
+}
+.ml5 {
+ margin-left: 5px;
+}
+.mt10 {
+ margin-top: 10px;
+}
+.mr10 {
+ margin-right: 10px;
+}
+.mb10 {
+ margin-bottom: 10px;
+}
+.ml0 {
+ margin-left: 10px;
+}
+.mt20 {
+ margin-top: 20px;
+}
+.mr20 {
+ margin-right: 20px;
+}
+.mb20 {
+ margin-bottom: 20px;
+}
+.m20 {
+ margin-left: 20px;
+}
+
+.el-dialog:not(.is-fullscreen){
+ margin-top: 6vh !important;
+}
+
+.el-table .el-table__header-wrapper th {
+ word-break: break-word;
+ background-color: #f8f8f9;
+ color: #515a6e;
+ height: 40px;
+ font-size: 13px;
+}
+
+/** 表单布局 **/
+.form-header {
+ font-size:15px;
+ color:#6379bb;
+ border-bottom:1px solid #ddd;
+ margin:8px 10px 25px 10px;
+ padding-bottom:5px
+}
+
+/** 表格布局 **/
+.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;
+}
+
+/* 拖拽列样式 */
+.sortable-ghost{
+ opacity: .8;
+ color: #fff!important;
+ background: #42b983!important;
+}
\ 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..56151f4
--- /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;
+}
+.quill-img {
+ 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/Doc/index.vue b/ruoyi-ui/src/components/RuoYi/Doc/index.vue
new file mode 100644
index 0000000..3915c29
--- /dev/null
+++ b/ruoyi-ui/src/components/RuoYi/Doc/index.vue
@@ -0,0 +1,21 @@
+<template>
+ <div>
+ <svg-icon icon-class="question" @click="goto"/>
+ </div>
+</template>
+
+<script>
+export default {
+ name: 'RuoYiDoc',
+ data() {
+ return {
+ url: 'http://doc.ruoyi.vip/ruoyi-vue'
+ }
+ },
+ methods: {
+ goto() {
+ window.open(this.url)
+ }
+ }
+}
+</script>
\ No newline at end of file
diff --git a/ruoyi-ui/src/components/RuoYi/Git/index.vue b/ruoyi-ui/src/components/RuoYi/Git/index.vue
new file mode 100644
index 0000000..2aab63c
--- /dev/null
+++ b/ruoyi-ui/src/components/RuoYi/Git/index.vue
@@ -0,0 +1,21 @@
+<template>
+ <div>
+ <svg-icon icon-class="github" @click="goto"/>
+ </div>
+</template>
+
+<script>
+export default {
+ name: 'RuoYiGit',
+ data() {
+ return {
+ url: 'https://gitee.com/y_project/RuoYi-Vue'
+ }
+ },
+ methods: {
+ goto() {
+ 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..6cd3ab9
--- /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 :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..22427a8
--- /dev/null
+++ b/ruoyi-ui/src/layout/components/Navbar.vue
@@ -0,0 +1,187 @@
+<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-git id="ruoyi-git" class="right-menu-item hover-effect" />
+ </el-tooltip>
+
+ <el-tooltip content="文档地址" effect="dark" placement="bottom">
+ <ruo-yi-doc id="ruoyi-doc" 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 @click.native="setting = true">
+ <span>布局设置</span>
+ </el-dropdown-item>
+ <el-dropdown-item divided @click.native="logout">
+ <span>退出登录</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 RuoYiGit from '@/components/RuoYi/Git'
+import RuoYiDoc from '@/components/RuoYi/Doc'
+
+export default {
+ components: {
+ Breadcrumb,
+ Hamburger,
+ Screenfull,
+ SizeSelect,
+ Search,
+ RuoYiGit,
+ RuoYiDoc
+ },
+ 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..1dfd793
--- /dev/null
+++ b/ruoyi-ui/src/layout/components/Sidebar/SidebarItem.vue
@@ -0,0 +1,93 @@
+<template>
+ <div v-if="!item.hidden">
+ <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..19e1912
--- /dev/null
+++ b/ruoyi-ui/src/layout/components/TagsView/index.vue
@@ -0,0 +1,289 @@
+<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="!isAffix(tag)?closeSelectedTag(tag):''"
+ @contextmenu.prevent.native="openMenu(tag,$event)"
+ >
+ {{ tag.title }}
+ <span v-if="!isAffix(tag)" 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="!isAffix(selectedTag)" @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
+ },
+ isAffix(tag) {
+ return tag.meta && tag.meta.affix
+ },
+ 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.fullPath)
+ } 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..661ed59
--- /dev/null
+++ b/ruoyi-ui/src/main.js
@@ -0,0 +1,72 @@
+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 { download } from '@/utils/request'
+
+import './assets/icons' // icon
+import './permission' // permission control
+import { getDicts } from "@/api/system/dict/data";
+import { getConfigKey } from "@/api/system/config";
+import { parseTime, resetForm, addDateRange, selectDictLabel, handleTree } from "@/utils/ruoyi";
+import Pagination from "@/components/Pagination";
+
+// 全局方法挂载
+Vue.prototype.getDicts = getDicts
+Vue.prototype.getConfigKey = getConfigKey
+Vue.prototype.parseTime = parseTime
+Vue.prototype.resetForm = resetForm
+Vue.prototype.addDateRange = addDateRange
+Vue.prototype.selectDictLabel = selectDictLabel
+Vue.prototype.download = download
+Vue.prototype.handleTree = handleTree
+
+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..acebbd0
--- /dev/null
+++ b/ruoyi-ui/src/router/index.js
@@ -0,0 +1,127 @@
+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: (resolve) => require(['@/views/redirect'], resolve)
+ }
+ ]
+ },
+ {
+ path: '/login',
+ component: (resolve) => require(['@/views/login'], resolve),
+ hidden: true
+ },
+ {
+ path: '/404',
+ component: (resolve) => require(['@/views/error/404'], resolve),
+ hidden: true
+ },
+ {
+ path: '/401',
+ component: (resolve) => require(['@/views/error/401'], resolve),
+ hidden: true
+ },
+ {
+ path: '',
+ component: Layout,
+ redirect: 'index',
+ children: [
+ {
+ path: 'index',
+ component: (resolve) => require(['@/views/index'], resolve),
+ name: '首页',
+ meta: { title: '首页', icon: 'dashboard', noCache: true, affix: true }
+ }
+ ]
+ },
+ {
+ path: '/user',
+ component: Layout,
+ hidden: true,
+ redirect: 'noredirect',
+ children: [
+ {
+ path: 'profile',
+ component: (resolve) => require(['@/views/system/user/profile/index'], resolve),
+ name: 'Profile',
+ meta: { title: '个人中心', icon: 'user' }
+ }
+ ]
+ },
+ {
+ path: '/dict',
+ component: Layout,
+ hidden: true,
+ children: [
+ {
+ path: 'type/data/:dictId(\\d+)',
+ component: (resolve) => require(['@/views/system/dict/data'], resolve),
+ name: 'Data',
+ meta: { title: '字典数据', icon: '' }
+ }
+ ]
+ },
+ {
+ path: '/job',
+ component: Layout,
+ hidden: true,
+ children: [
+ {
+ path: 'log',
+ component: (resolve) => require(['@/views/monitor/job/log'], resolve),
+ name: 'JobLog',
+ meta: { title: '调度日志' }
+ }
+ ]
+ },
+ {
+ path: '/gen',
+ component: Layout,
+ hidden: true,
+ children: [
+ {
+ path: 'edit',
+ component: (resolve) => require(['@/views/tool/gen/editTable'], resolve),
+ name: 'GenEdit',
+ meta: { title: '修改生成配置' }
+ }
+ ]
+ }
+]
+
+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..deed451
--- /dev/null
+++ b/ruoyi-ui/src/store/modules/permission.js
@@ -0,0 +1,54 @@
+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)
+ accessedRoutes.push({ path: '*', redirect: '/404', hidden: true })
+ 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 (resolve) => require([`@/views/${view}`], resolve)
+}
+
+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..abec622
--- /dev/null
+++ b/ruoyi-ui/src/store/modules/tagsView.js
@@ -0,0 +1,159 @@
+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) => {
+ const index = state.cachedViews.indexOf(view.name)
+ index > -1 && state.cachedViews.splice(index, 1)
+ },
+
+ 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) => {
+ const index = state.cachedViews.indexOf(view.name)
+ if (index > -1) {
+ state.cachedViews = state.cachedViews.slice(index, index + 1)
+ } else {
+ state.cachedViews = []
+ }
+ },
+
+ 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..5c8a33f
--- /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.access_token)
+ commit('SET_TOKEN', res.access_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/errorCode.js b/ruoyi-ui/src/utils/errorCode.js
new file mode 100644
index 0000000..8f27f7a
--- /dev/null
+++ b/ruoyi-ui/src/utils/errorCode.js
@@ -0,0 +1,6 @@
+export default {
+ '401': '认证失败,无法访问系统资源',
+ '403': '当前操作没有权限',
+ '404': '访问资源不存在',
+ 'default': '系统未知错误,请反馈给管理员'
+}
diff --git a/ruoyi-ui/src/utils/generator/config.js b/ruoyi-ui/src/utils/generator/config.js
new file mode 100644
index 0000000..471f838
--- /dev/null
+++ b/ruoyi-ui/src/utils/generator/config.js
@@ -0,0 +1,423 @@
+export const formConf = {
+ formRef: 'elForm',
+ formModel: 'formData',
+ size: 'medium',
+ labelPosition: 'right',
+ labelWidth: 100,
+ formRules: 'rules',
+ gutter: 15,
+ disabled: false,
+ span: 24,
+ formBtns: true
+}
+
+export const inputComponents = [
+ {
+ label: '单行文本',
+ tag: 'el-input',
+ tagIcon: 'input',
+ placeholder: '请输入',
+ defaultValue: undefined,
+ span: 24,
+ labelWidth: null,
+ style: { width: '100%' },
+ clearable: true,
+ prepend: '',
+ append: '',
+ 'prefix-icon': '',
+ 'suffix-icon': '',
+ maxlength: null,
+ 'show-word-limit': false,
+ readonly: false,
+ disabled: false,
+ required: true,
+ regList: [],
+ changeTag: true,
+ document: 'https://element.eleme.cn/#/zh-CN/component/input'
+ },
+ {
+ label: '多行文本',
+ tag: 'el-input',
+ tagIcon: 'textarea',
+ type: 'textarea',
+ placeholder: '请输入',
+ defaultValue: undefined,
+ span: 24,
+ labelWidth: null,
+ autosize: {
+ minRows: 4,
+ maxRows: 4
+ },
+ style: { width: '100%' },
+ maxlength: null,
+ 'show-word-limit': false,
+ readonly: false,
+ disabled: false,
+ required: true,
+ regList: [],
+ changeTag: true,
+ document: 'https://element.eleme.cn/#/zh-CN/component/input'
+ },
+ {
+ label: '密码',
+ tag: 'el-input',
+ tagIcon: 'password',
+ placeholder: '请输入',
+ defaultValue: undefined,
+ span: 24,
+ 'show-password': true,
+ labelWidth: null,
+ style: { width: '100%' },
+ clearable: true,
+ prepend: '',
+ append: '',
+ 'prefix-icon': '',
+ 'suffix-icon': '',
+ maxlength: null,
+ 'show-word-limit': false,
+ readonly: false,
+ disabled: false,
+ required: true,
+ regList: [],
+ changeTag: true,
+ document: 'https://element.eleme.cn/#/zh-CN/component/input'
+ },
+ {
+ label: '计数器',
+ tag: 'el-input-number',
+ tagIcon: 'number',
+ placeholder: '',
+ defaultValue: undefined,
+ span: 24,
+ labelWidth: null,
+ min: undefined,
+ max: undefined,
+ step: undefined,
+ 'step-strictly': false,
+ precision: undefined,
+ 'controls-position': '',
+ disabled: false,
+ required: true,
+ regList: [],
+ changeTag: true,
+ document: 'https://element.eleme.cn/#/zh-CN/component/input-number'
+ }
+]
+
+export const selectComponents = [
+ {
+ label: '下拉选择',
+ tag: 'el-select',
+ tagIcon: 'select',
+ placeholder: '请选择',
+ defaultValue: undefined,
+ span: 24,
+ labelWidth: null,
+ style: { width: '100%' },
+ clearable: true,
+ disabled: false,
+ required: true,
+ filterable: false,
+ multiple: false,
+ options: [{
+ label: '选项一',
+ value: 1
+ }, {
+ label: '选项二',
+ value: 2
+ }],
+ regList: [],
+ changeTag: true,
+ document: 'https://element.eleme.cn/#/zh-CN/component/select'
+ },
+ {
+ label: '级联选择',
+ tag: 'el-cascader',
+ tagIcon: 'cascader',
+ placeholder: '请选择',
+ defaultValue: [],
+ span: 24,
+ labelWidth: null,
+ style: { width: '100%' },
+ props: {
+ props: {
+ multiple: false
+ }
+ },
+ 'show-all-levels': true,
+ disabled: false,
+ clearable: true,
+ filterable: false,
+ required: true,
+ options: [{
+ id: 1,
+ value: 1,
+ label: '选项1',
+ children: [{
+ id: 2,
+ value: 2,
+ label: '选项1-1'
+ }]
+ }],
+ dataType: 'dynamic',
+ labelKey: 'label',
+ valueKey: 'value',
+ childrenKey: 'children',
+ separator: '/',
+ regList: [],
+ changeTag: true,
+ document: 'https://element.eleme.cn/#/zh-CN/component/cascader'
+ },
+ {
+ label: '单选框组',
+ tag: 'el-radio-group',
+ tagIcon: 'radio',
+ defaultValue: undefined,
+ span: 24,
+ labelWidth: null,
+ style: {},
+ optionType: 'default',
+ border: false,
+ size: 'medium',
+ disabled: false,
+ required: true,
+ options: [{
+ label: '选项一',
+ value: 1
+ }, {
+ label: '选项二',
+ value: 2
+ }],
+ regList: [],
+ changeTag: true,
+ document: 'https://element.eleme.cn/#/zh-CN/component/radio'
+ },
+ {
+ label: '多选框组',
+ tag: 'el-checkbox-group',
+ tagIcon: 'checkbox',
+ defaultValue: [],
+ span: 24,
+ labelWidth: null,
+ style: {},
+ optionType: 'default',
+ border: false,
+ size: 'medium',
+ disabled: false,
+ required: true,
+ options: [{
+ label: '选项一',
+ value: 1
+ }, {
+ label: '选项二',
+ value: 2
+ }],
+ regList: [],
+ changeTag: true,
+ document: 'https://element.eleme.cn/#/zh-CN/component/checkbox'
+ },
+ {
+ label: '开关',
+ tag: 'el-switch',
+ tagIcon: 'switch',
+ defaultValue: false,
+ span: 24,
+ labelWidth: null,
+ style: {},
+ disabled: false,
+ required: true,
+ 'active-text': '',
+ 'inactive-text': '',
+ 'active-color': null,
+ 'inactive-color': null,
+ 'active-value': true,
+ 'inactive-value': false,
+ regList: [],
+ changeTag: true,
+ document: 'https://element.eleme.cn/#/zh-CN/component/switch'
+ },
+ {
+ label: '滑块',
+ tag: 'el-slider',
+ tagIcon: 'slider',
+ defaultValue: null,
+ span: 24,
+ labelWidth: null,
+ disabled: false,
+ required: true,
+ min: 0,
+ max: 100,
+ step: 1,
+ 'show-stops': false,
+ range: false,
+ regList: [],
+ changeTag: true,
+ document: 'https://element.eleme.cn/#/zh-CN/component/slider'
+ },
+ {
+ label: '时间选择',
+ tag: 'el-time-picker',
+ tagIcon: 'time',
+ placeholder: '请选择',
+ defaultValue: null,
+ span: 24,
+ labelWidth: null,
+ style: { width: '100%' },
+ disabled: false,
+ clearable: true,
+ required: true,
+ 'picker-options': {
+ selectableRange: '00:00:00-23:59:59'
+ },
+ format: 'HH:mm:ss',
+ 'value-format': 'HH:mm:ss',
+ regList: [],
+ changeTag: true,
+ document: 'https://element.eleme.cn/#/zh-CN/component/time-picker'
+ },
+ {
+ label: '时间范围',
+ tag: 'el-time-picker',
+ tagIcon: 'time-range',
+ defaultValue: null,
+ span: 24,
+ labelWidth: null,
+ style: { width: '100%' },
+ disabled: false,
+ clearable: true,
+ required: true,
+ 'is-range': true,
+ 'range-separator': '至',
+ 'start-placeholder': '开始时间',
+ 'end-placeholder': '结束时间',
+ format: 'HH:mm:ss',
+ 'value-format': 'HH:mm:ss',
+ regList: [],
+ changeTag: true,
+ document: 'https://element.eleme.cn/#/zh-CN/component/time-picker'
+ },
+ {
+ label: '日期选择',
+ tag: 'el-date-picker',
+ tagIcon: 'date',
+ placeholder: '请选择',
+ defaultValue: null,
+ type: 'date',
+ span: 24,
+ labelWidth: null,
+ style: { width: '100%' },
+ disabled: false,
+ clearable: true,
+ required: true,
+ format: 'yyyy-MM-dd',
+ 'value-format': 'yyyy-MM-dd',
+ readonly: false,
+ regList: [],
+ changeTag: true,
+ document: 'https://element.eleme.cn/#/zh-CN/component/date-picker'
+ },
+ {
+ label: '日期范围',
+ tag: 'el-date-picker',
+ tagIcon: 'date-range',
+ defaultValue: null,
+ span: 24,
+ labelWidth: null,
+ style: { width: '100%' },
+ type: 'daterange',
+ 'range-separator': '至',
+ 'start-placeholder': '开始日期',
+ 'end-placeholder': '结束日期',
+ disabled: false,
+ clearable: true,
+ required: true,
+ format: 'yyyy-MM-dd',
+ 'value-format': 'yyyy-MM-dd',
+ readonly: false,
+ regList: [],
+ changeTag: true,
+ document: 'https://element.eleme.cn/#/zh-CN/component/date-picker'
+ },
+ {
+ label: '评分',
+ tag: 'el-rate',
+ tagIcon: 'rate',
+ defaultValue: 0,
+ span: 24,
+ labelWidth: null,
+ style: {},
+ max: 5,
+ 'allow-half': false,
+ 'show-text': false,
+ 'show-score': false,
+ disabled: false,
+ required: true,
+ regList: [],
+ changeTag: true,
+ document: 'https://element.eleme.cn/#/zh-CN/component/rate'
+ },
+ {
+ label: '颜色选择',
+ tag: 'el-color-picker',
+ tagIcon: 'color',
+ defaultValue: null,
+ labelWidth: null,
+ 'show-alpha': false,
+ 'color-format': '',
+ disabled: false,
+ required: true,
+ size: 'medium',
+ regList: [],
+ changeTag: true,
+ document: 'https://element.eleme.cn/#/zh-CN/component/color-picker'
+ },
+ {
+ label: '上传',
+ tag: 'el-upload',
+ tagIcon: 'upload',
+ action: 'https://jsonplaceholder.typicode.com/posts/',
+ defaultValue: null,
+ labelWidth: null,
+ disabled: false,
+ required: true,
+ accept: '',
+ name: 'file',
+ 'auto-upload': true,
+ showTip: false,
+ buttonText: '点击上传',
+ fileSize: 2,
+ sizeUnit: 'MB',
+ 'list-type': 'text',
+ multiple: false,
+ regList: [],
+ changeTag: true,
+ document: 'https://element.eleme.cn/#/zh-CN/component/upload'
+ }
+]
+
+export const layoutComponents = [
+ {
+ layout: 'rowFormItem',
+ tagIcon: 'row',
+ type: 'default',
+ justify: 'start',
+ align: 'top',
+ label: '行容器',
+ layoutTree: true,
+ children: [],
+ document: 'https://element.eleme.cn/#/zh-CN/component/layout'
+ }
+]
+
+// 组件rule的触发方式,无触发方式的组件不生成rule
+export const trigger = {
+ 'el-input': 'blur',
+ 'el-input-number': 'blur',
+ 'el-select': 'change',
+ 'el-radio-group': 'change',
+ 'el-checkbox-group': 'change',
+ 'el-cascader': 'change',
+ 'el-time-picker': 'change',
+ 'el-date-picker': 'change',
+ 'el-rate': 'change'
+}
diff --git a/ruoyi-ui/src/utils/generator/css.js b/ruoyi-ui/src/utils/generator/css.js
new file mode 100644
index 0000000..0d7f075
--- /dev/null
+++ b/ruoyi-ui/src/utils/generator/css.js
@@ -0,0 +1,18 @@
+const styles = {
+ 'el-rate': '.el-rate{display: inline-block; vertical-align: text-top;}',
+ 'el-upload': '.el-upload__tip{line-height: 1.2;}'
+}
+
+function addCss(cssList, el) {
+ const css = styles[el.tag]
+ css && cssList.indexOf(css) === -1 && cssList.push(css)
+ if (el.children) {
+ el.children.forEach(el2 => addCss(cssList, el2))
+ }
+}
+
+export function makeUpCss(conf) {
+ const cssList = []
+ conf.fields.forEach(el => addCss(cssList, el))
+ return cssList.join('\n')
+}
diff --git a/ruoyi-ui/src/utils/generator/drawingDefalut.js b/ruoyi-ui/src/utils/generator/drawingDefalut.js
new file mode 100644
index 0000000..5f7d1c4
--- /dev/null
+++ b/ruoyi-ui/src/utils/generator/drawingDefalut.js
@@ -0,0 +1,29 @@
+export default [
+ {
+ layout: 'colFormItem',
+ tagIcon: 'input',
+ label: '手机号',
+ vModel: 'mobile',
+ formId: 6,
+ tag: 'el-input',
+ placeholder: '请输入手机号',
+ defaultValue: '',
+ span: 24,
+ style: { width: '100%' },
+ clearable: true,
+ prepend: '',
+ append: '',
+ 'prefix-icon': 'el-icon-mobile',
+ 'suffix-icon': '',
+ maxlength: 11,
+ 'show-word-limit': true,
+ readonly: false,
+ disabled: false,
+ required: true,
+ changeTag: true,
+ regList: [{
+ pattern: '/^1(3|4|5|7|8|9)\\d{9}$/',
+ message: '手机号格式错误'
+ }]
+ }
+]
diff --git a/ruoyi-ui/src/utils/generator/html.js b/ruoyi-ui/src/utils/generator/html.js
new file mode 100644
index 0000000..8362ae4
--- /dev/null
+++ b/ruoyi-ui/src/utils/generator/html.js
@@ -0,0 +1,338 @@
+/* eslint-disable max-len */
+import { trigger } from './config'
+
+let confGlobal
+let someSpanIsNot24
+
+export function dialogWrapper(str) {
+ return `<el-dialog v-bind="$attrs" v-on="$listeners" @open="onOpen" @close="onClose" title="Dialog Titile">
+ ${str}
+ <div slot="footer">
+ <el-button @click="close">取消</el-button>
+ <el-button type="primary" @click="handelConfirm">确定</el-button>
+ </div>
+ </el-dialog>`
+}
+
+export function vueTemplate(str) {
+ return `<template>
+ <div>
+ ${str}
+ </div>
+ </template>`
+}
+
+export function vueScript(str) {
+ return `<script>
+ ${str}
+ </script>`
+}
+
+export function cssStyle(cssStr) {
+ return `<style>
+ ${cssStr}
+ </style>`
+}
+
+function buildFormTemplate(conf, child, type) {
+ let labelPosition = ''
+ if (conf.labelPosition !== 'right') {
+ labelPosition = `label-position="${conf.labelPosition}"`
+ }
+ const disabled = conf.disabled ? `:disabled="${conf.disabled}"` : ''
+ let str = `<el-form ref="${conf.formRef}" :model="${conf.formModel}" :rules="${conf.formRules}" size="${conf.size}" ${disabled} label-width="${conf.labelWidth}px" ${labelPosition}>
+ ${child}
+ ${buildFromBtns(conf, type)}
+ </el-form>`
+ if (someSpanIsNot24) {
+ str = `<el-row :gutter="${conf.gutter}">
+ ${str}
+ </el-row>`
+ }
+ return str
+}
+
+function buildFromBtns(conf, type) {
+ let str = ''
+ if (conf.formBtns && type === 'file') {
+ str = `<el-form-item size="large">
+ <el-button type="primary" @click="submitForm">提交</el-button>
+ <el-button @click="resetForm">重置</el-button>
+ </el-form-item>`
+ if (someSpanIsNot24) {
+ str = `<el-col :span="24">
+ ${str}
+ </el-col>`
+ }
+ }
+ return str
+}
+
+// span不为24的用el-col包裹
+function colWrapper(element, str) {
+ if (someSpanIsNot24 || element.span !== 24) {
+ return `<el-col :span="${element.span}">
+ ${str}
+ </el-col>`
+ }
+ return str
+}
+
+const layouts = {
+ colFormItem(element) {
+ let labelWidth = ''
+ if (element.labelWidth && element.labelWidth !== confGlobal.labelWidth) {
+ labelWidth = `label-width="${element.labelWidth}px"`
+ }
+ const required = !trigger[element.tag] && element.required ? 'required' : ''
+ const tagDom = tags[element.tag] ? tags[element.tag](element) : null
+ let str = `<el-form-item ${labelWidth} label="${element.label}" prop="${element.vModel}" ${required}>
+ ${tagDom}
+ </el-form-item>`
+ str = colWrapper(element, str)
+ return str
+ },
+ rowFormItem(element) {
+ const type = element.type === 'default' ? '' : `type="${element.type}"`
+ const justify = element.type === 'default' ? '' : `justify="${element.justify}"`
+ const align = element.type === 'default' ? '' : `align="${element.align}"`
+ const gutter = element.gutter ? `gutter="${element.gutter}"` : ''
+ const children = element.children.map(el => layouts[el.layout](el))
+ let str = `<el-row ${type} ${justify} ${align} ${gutter}>
+ ${children.join('\n')}
+ </el-row>`
+ str = colWrapper(element, str)
+ return str
+ }
+}
+
+const tags = {
+ 'el-input': el => {
+ const {
+ disabled, vModel, clearable, placeholder, width
+ } = attrBuilder(el)
+ const maxlength = el.maxlength ? `:maxlength="${el.maxlength}"` : ''
+ const showWordLimit = el['show-word-limit'] ? 'show-word-limit' : ''
+ const readonly = el.readonly ? 'readonly' : ''
+ const prefixIcon = el['prefix-icon'] ? `prefix-icon='${el['prefix-icon']}'` : ''
+ const suffixIcon = el['suffix-icon'] ? `suffix-icon='${el['suffix-icon']}'` : ''
+ const showPassword = el['show-password'] ? 'show-password' : ''
+ const type = el.type ? `type="${el.type}"` : ''
+ const autosize = el.autosize && el.autosize.minRows
+ ? `:autosize="{minRows: ${el.autosize.minRows}, maxRows: ${el.autosize.maxRows}}"`
+ : ''
+ let child = buildElInputChild(el)
+
+ if (child) child = `\n${child}\n` // 换行
+ return `<${el.tag} ${vModel} ${type} ${placeholder} ${maxlength} ${showWordLimit} ${readonly} ${disabled} ${clearable} ${prefixIcon} ${suffixIcon} ${showPassword} ${autosize} ${width}>${child}</${el.tag}>`
+ },
+ 'el-input-number': el => {
+ const { disabled, vModel, placeholder } = attrBuilder(el)
+ const controlsPosition = el['controls-position'] ? `controls-position=${el['controls-position']}` : ''
+ const min = el.min ? `:min='${el.min}'` : ''
+ const max = el.max ? `:max='${el.max}'` : ''
+ const step = el.step ? `:step='${el.step}'` : ''
+ const stepStrictly = el['step-strictly'] ? 'step-strictly' : ''
+ const precision = el.precision ? `:precision='${el.precision}'` : ''
+
+ return `<${el.tag} ${vModel} ${placeholder} ${step} ${stepStrictly} ${precision} ${controlsPosition} ${min} ${max} ${disabled}></${el.tag}>`
+ },
+ 'el-select': el => {
+ const {
+ disabled, vModel, clearable, placeholder, width
+ } = attrBuilder(el)
+ const filterable = el.filterable ? 'filterable' : ''
+ const multiple = el.multiple ? 'multiple' : ''
+ let child = buildElSelectChild(el)
+
+ if (child) child = `\n${child}\n` // 换行
+ return `<${el.tag} ${vModel} ${placeholder} ${disabled} ${multiple} ${filterable} ${clearable} ${width}>${child}</${el.tag}>`
+ },
+ 'el-radio-group': el => {
+ const { disabled, vModel } = attrBuilder(el)
+ const size = `size="${el.size}"`
+ let child = buildElRadioGroupChild(el)
+
+ if (child) child = `\n${child}\n` // 换行
+ return `<${el.tag} ${vModel} ${size} ${disabled}>${child}</${el.tag}>`
+ },
+ 'el-checkbox-group': el => {
+ const { disabled, vModel } = attrBuilder(el)
+ const size = `size="${el.size}"`
+ const min = el.min ? `:min="${el.min}"` : ''
+ const max = el.max ? `:max="${el.max}"` : ''
+ let child = buildElCheckboxGroupChild(el)
+
+ if (child) child = `\n${child}\n` // 换行
+ return `<${el.tag} ${vModel} ${min} ${max} ${size} ${disabled}>${child}</${el.tag}>`
+ },
+ 'el-switch': el => {
+ const { disabled, vModel } = attrBuilder(el)
+ const activeText = el['active-text'] ? `active-text="${el['active-text']}"` : ''
+ const inactiveText = el['inactive-text'] ? `inactive-text="${el['inactive-text']}"` : ''
+ const activeColor = el['active-color'] ? `active-color="${el['active-color']}"` : ''
+ const inactiveColor = el['inactive-color'] ? `inactive-color="${el['inactive-color']}"` : ''
+ const activeValue = el['active-value'] !== true ? `:active-value='${JSON.stringify(el['active-value'])}'` : ''
+ const inactiveValue = el['inactive-value'] !== false ? `:inactive-value='${JSON.stringify(el['inactive-value'])}'` : ''
+
+ return `<${el.tag} ${vModel} ${activeText} ${inactiveText} ${activeColor} ${inactiveColor} ${activeValue} ${inactiveValue} ${disabled}></${el.tag}>`
+ },
+ 'el-cascader': el => {
+ const {
+ disabled, vModel, clearable, placeholder, width
+ } = attrBuilder(el)
+ const options = el.options ? `:options="${el.vModel}Options"` : ''
+ const props = el.props ? `:props="${el.vModel}Props"` : ''
+ const showAllLevels = el['show-all-levels'] ? '' : ':show-all-levels="false"'
+ const filterable = el.filterable ? 'filterable' : ''
+ const separator = el.separator === '/' ? '' : `separator="${el.separator}"`
+
+ return `<${el.tag} ${vModel} ${options} ${props} ${width} ${showAllLevels} ${placeholder} ${separator} ${filterable} ${clearable} ${disabled}></${el.tag}>`
+ },
+ 'el-slider': el => {
+ const { disabled, vModel } = attrBuilder(el)
+ const min = el.min ? `:min='${el.min}'` : ''
+ const max = el.max ? `:max='${el.max}'` : ''
+ const step = el.step ? `:step='${el.step}'` : ''
+ const range = el.range ? 'range' : ''
+ const showStops = el['show-stops'] ? `:show-stops="${el['show-stops']}"` : ''
+
+ return `<${el.tag} ${min} ${max} ${step} ${vModel} ${range} ${showStops} ${disabled}></${el.tag}>`
+ },
+ 'el-time-picker': el => {
+ const {
+ disabled, vModel, clearable, placeholder, width
+ } = attrBuilder(el)
+ const startPlaceholder = el['start-placeholder'] ? `start-placeholder="${el['start-placeholder']}"` : ''
+ const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : ''
+ const rangeSeparator = el['range-separator'] ? `range-separator="${el['range-separator']}"` : ''
+ const isRange = el['is-range'] ? 'is-range' : ''
+ const format = el.format ? `format="${el.format}"` : ''
+ const valueFormat = el['value-format'] ? `value-format="${el['value-format']}"` : ''
+ const pickerOptions = el['picker-options'] ? `:picker-options='${JSON.stringify(el['picker-options'])}'` : ''
+
+ return `<${el.tag} ${vModel} ${isRange} ${format} ${valueFormat} ${pickerOptions} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${disabled}></${el.tag}>`
+ },
+ 'el-date-picker': el => {
+ const {
+ disabled, vModel, clearable, placeholder, width
+ } = attrBuilder(el)
+ const startPlaceholder = el['start-placeholder'] ? `start-placeholder="${el['start-placeholder']}"` : ''
+ const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : ''
+ const rangeSeparator = el['range-separator'] ? `range-separator="${el['range-separator']}"` : ''
+ const format = el.format ? `format="${el.format}"` : ''
+ const valueFormat = el['value-format'] ? `value-format="${el['value-format']}"` : ''
+ const type = el.type === 'date' ? '' : `type="${el.type}"`
+ const readonly = el.readonly ? 'readonly' : ''
+
+ return `<${el.tag} ${type} ${vModel} ${format} ${valueFormat} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${readonly} ${disabled}></${el.tag}>`
+ },
+ 'el-rate': el => {
+ const { disabled, vModel } = attrBuilder(el)
+ const max = el.max ? `:max='${el.max}'` : ''
+ const allowHalf = el['allow-half'] ? 'allow-half' : ''
+ const showText = el['show-text'] ? 'show-text' : ''
+ const showScore = el['show-score'] ? 'show-score' : ''
+
+ return `<${el.tag} ${vModel} ${allowHalf} ${showText} ${showScore} ${disabled}></${el.tag}>`
+ },
+ 'el-color-picker': el => {
+ const { disabled, vModel } = attrBuilder(el)
+ const size = `size="${el.size}"`
+ const showAlpha = el['show-alpha'] ? 'show-alpha' : ''
+ const colorFormat = el['color-format'] ? `color-format="${el['color-format']}"` : ''
+
+ return `<${el.tag} ${vModel} ${size} ${showAlpha} ${colorFormat} ${disabled}></${el.tag}>`
+ },
+ 'el-upload': el => {
+ const disabled = el.disabled ? ':disabled=\'true\'' : ''
+ const action = el.action ? `:action="${el.vModel}Action"` : ''
+ const multiple = el.multiple ? 'multiple' : ''
+ const listType = el['list-type'] !== 'text' ? `list-type="${el['list-type']}"` : ''
+ const accept = el.accept ? `accept="${el.accept}"` : ''
+ const name = el.name !== 'file' ? `name="${el.name}"` : ''
+ const autoUpload = el['auto-upload'] === false ? ':auto-upload="false"' : ''
+ const beforeUpload = `:before-upload="${el.vModel}BeforeUpload"`
+ const fileList = `:file-list="${el.vModel}fileList"`
+ const ref = `ref="${el.vModel}"`
+ let child = buildElUploadChild(el)
+
+ if (child) child = `\n${child}\n` // 换行
+ return `<${el.tag} ${ref} ${fileList} ${action} ${autoUpload} ${multiple} ${beforeUpload} ${listType} ${accept} ${name} ${disabled}>${child}</${el.tag}>`
+ }
+}
+
+function attrBuilder(el) {
+ return {
+ vModel: `v-model="${confGlobal.formModel}.${el.vModel}"`,
+ clearable: el.clearable ? 'clearable' : '',
+ placeholder: el.placeholder ? `placeholder="${el.placeholder}"` : '',
+ width: el.style && el.style.width ? ':style="{width: \'100%\'}"' : '',
+ disabled: el.disabled ? ':disabled=\'true\'' : ''
+ }
+}
+
+// el-input innerHTML
+function buildElInputChild(conf) {
+ const children = []
+ if (conf.prepend) {
+ children.push(`<template slot="prepend">${conf.prepend}</template>`)
+ }
+ if (conf.append) {
+ children.push(`<template slot="append">${conf.append}</template>`)
+ }
+ return children.join('\n')
+}
+
+function buildElSelectChild(conf) {
+ const children = []
+ if (conf.options && conf.options.length) {
+ children.push(`<el-option v-for="(item, index) in ${conf.vModel}Options" :key="index" :label="item.label" :value="item.value" :disabled="item.disabled"></el-option>`)
+ }
+ return children.join('\n')
+}
+
+function buildElRadioGroupChild(conf) {
+ const children = []
+ if (conf.options && conf.options.length) {
+ const tag = conf.optionType === 'button' ? 'el-radio-button' : 'el-radio'
+ const border = conf.border ? 'border' : ''
+ children.push(`<${tag} v-for="(item, index) in ${conf.vModel}Options" :key="index" :label="item.value" :disabled="item.disabled" ${border}>{{item.label}}</${tag}>`)
+ }
+ return children.join('\n')
+}
+
+function buildElCheckboxGroupChild(conf) {
+ const children = []
+ if (conf.options && conf.options.length) {
+ const tag = conf.optionType === 'button' ? 'el-checkbox-button' : 'el-checkbox'
+ const border = conf.border ? 'border' : ''
+ children.push(`<${tag} v-for="(item, index) in ${conf.vModel}Options" :key="index" :label="item.value" :disabled="item.disabled" ${border}>{{item.label}}</${tag}>`)
+ }
+ return children.join('\n')
+}
+
+function buildElUploadChild(conf) {
+ const list = []
+ if (conf['list-type'] === 'picture-card') list.push('<i class="el-icon-plus"></i>')
+ else list.push(`<el-button size="small" type="primary" icon="el-icon-upload">${conf.buttonText}</el-button>`)
+ if (conf.showTip) list.push(`<div slot="tip" class="el-upload__tip">只能上传不超过 ${conf.fileSize}${conf.sizeUnit} 的${conf.accept}文件</div>`)
+ return list.join('\n')
+}
+
+export function makeUpHtml(conf, type) {
+ const htmlList = []
+ confGlobal = conf
+ someSpanIsNot24 = conf.fields.some(item => item.span !== 24)
+ conf.fields.forEach(el => {
+ htmlList.push(layouts[el.layout](el))
+ })
+ const htmlStr = htmlList.join('\n')
+
+ let temp = buildFormTemplate(conf, htmlStr, type)
+ if (type === 'dialog') {
+ temp = dialogWrapper(temp)
+ }
+ confGlobal = null
+ return temp
+}
diff --git a/ruoyi-ui/src/utils/generator/icon.json b/ruoyi-ui/src/utils/generator/icon.json
new file mode 100644
index 0000000..2d9999a
--- /dev/null
+++ b/ruoyi-ui/src/utils/generator/icon.json
@@ -0,0 +1 @@
+["platform-eleme","eleme","delete-solid","delete","s-tools","setting","user-solid","user","phone","phone-outline","more","more-outline","star-on","star-off","s-goods","goods","warning","warning-outline","question","info","remove","circle-plus","success","error","zoom-in","zoom-out","remove-outline","circle-plus-outline","circle-check","circle-close","s-help","help","minus","plus","check","close","picture","picture-outline","picture-outline-round","upload","upload2","download","camera-solid","camera","video-camera-solid","video-camera","message-solid","bell","s-cooperation","s-order","s-platform","s-fold","s-unfold","s-operation","s-promotion","s-home","s-release","s-ticket","s-management","s-open","s-shop","s-marketing","s-flag","s-comment","s-finance","s-claim","s-custom","s-opportunity","s-data","s-check","s-grid","menu","share","d-caret","caret-left","caret-right","caret-bottom","caret-top","bottom-left","bottom-right","back","right","bottom","top","top-left","top-right","arrow-left","arrow-right","arrow-down","arrow-up","d-arrow-left","d-arrow-right","video-pause","video-play","refresh","refresh-right","refresh-left","finished","sort","sort-up","sort-down","rank","loading","view","c-scale-to-original","date","edit","edit-outline","folder","folder-opened","folder-add","folder-remove","folder-delete","folder-checked","tickets","document-remove","document-delete","document-copy","document-checked","document","document-add","printer","paperclip","takeaway-box","search","monitor","attract","mobile","scissors","umbrella","headset","brush","mouse","coordinate","magic-stick","reading","data-line","data-board","pie-chart","data-analysis","collection-tag","film","suitcase","suitcase-1","receiving","collection","files","notebook-1","notebook-2","toilet-paper","office-building","school","table-lamp","house","no-smoking","smoking","shopping-cart-full","shopping-cart-1","shopping-cart-2","shopping-bag-1","shopping-bag-2","sold-out","sell","present","box","bank-card","money","coin","wallet","discount","price-tag","news","guide","male","female","thumb","cpu","link","connection","open","turn-off","set-up","chat-round","chat-line-round","chat-square","chat-dot-round","chat-dot-square","chat-line-square","message","postcard","position","turn-off-microphone","microphone","close-notification","bangzhu","time","odometer","crop","aim","switch-button","full-screen","copy-document","mic","stopwatch","medal-1","medal","trophy","trophy-1","first-aid-kit","discover","place","location","location-outline","location-information","add-location","delete-location","map-location","alarm-clock","timer","watch-1","watch","lock","unlock","key","service","mobile-phone","bicycle","truck","ship","basketball","football","soccer","baseball","wind-power","light-rain","lightning","heavy-rain","sunrise","sunrise-1","sunset","sunny","cloudy","partly-cloudy","cloudy-and-sunny","moon","moon-night","dish","dish-1","food","chicken","fork-spoon","knife-fork","burger","tableware","sugar","dessert","ice-cream","hot-water","water-cup","coffee-cup","cold-drink","goblet","goblet-full","goblet-square","goblet-square-full","refrigerator","grape","watermelon","cherry","apple","pear","orange","coffee","ice-tea","ice-drink","milk-tea","potato-strips","lollipop","ice-cream-square","ice-cream-round"]
\ No newline at end of file
diff --git a/ruoyi-ui/src/utils/generator/js.js b/ruoyi-ui/src/utils/generator/js.js
new file mode 100644
index 0000000..81afc70
--- /dev/null
+++ b/ruoyi-ui/src/utils/generator/js.js
@@ -0,0 +1,236 @@
+import { isArray } from 'util'
+import { exportDefault, titleCase } from '@/utils/index'
+import { trigger } from './config'
+
+const units = {
+ KB: '1024',
+ MB: '1024 / 1024',
+ GB: '1024 / 1024 / 1024'
+}
+let confGlobal
+const inheritAttrs = {
+ file: '',
+ dialog: 'inheritAttrs: false,'
+}
+
+
+export function makeUpJs(conf, type) {
+ confGlobal = conf = JSON.parse(JSON.stringify(conf))
+ const dataList = []
+ const ruleList = []
+ const optionsList = []
+ const propsList = []
+ const methodList = mixinMethod(type)
+ const uploadVarList = []
+
+ conf.fields.forEach(el => {
+ buildAttributes(el, dataList, ruleList, optionsList, methodList, propsList, uploadVarList)
+ })
+
+ const script = buildexport(
+ conf,
+ type,
+ dataList.join('\n'),
+ ruleList.join('\n'),
+ optionsList.join('\n'),
+ uploadVarList.join('\n'),
+ propsList.join('\n'),
+ methodList.join('\n')
+ )
+ confGlobal = null
+ return script
+}
+
+function buildAttributes(el, dataList, ruleList, optionsList, methodList, propsList, uploadVarList) {
+ buildData(el, dataList)
+ buildRules(el, ruleList)
+
+ if (el.options && el.options.length) {
+ buildOptions(el, optionsList)
+ if (el.dataType === 'dynamic') {
+ const model = `${el.vModel}Options`
+ const options = titleCase(model)
+ buildOptionMethod(`get${options}`, model, methodList)
+ }
+ }
+
+ if (el.props && el.props.props) {
+ buildProps(el, propsList)
+ }
+
+ if (el.action && el.tag === 'el-upload') {
+ uploadVarList.push(
+ `${el.vModel}Action: '${el.action}',
+ ${el.vModel}fileList: [],`
+ )
+ methodList.push(buildBeforeUpload(el))
+ if (!el['auto-upload']) {
+ methodList.push(buildSubmitUpload(el))
+ }
+ }
+
+ if (el.children) {
+ el.children.forEach(el2 => {
+ buildAttributes(el2, dataList, ruleList, optionsList, methodList, propsList, uploadVarList)
+ })
+ }
+}
+
+function mixinMethod(type) {
+ const list = []; const
+ minxins = {
+ file: confGlobal.formBtns ? {
+ submitForm: `submitForm() {
+ this.$refs['${confGlobal.formRef}'].validate(valid => {
+ if(!valid) return
+ // TODO 提交表单
+ })
+ },`,
+ resetForm: `resetForm() {
+ this.$refs['${confGlobal.formRef}'].resetFields()
+ },`
+ } : null,
+ dialog: {
+ onOpen: 'onOpen() {},',
+ onClose: `onClose() {
+ this.$refs['${confGlobal.formRef}'].resetFields()
+ },`,
+ close: `close() {
+ this.$emit('update:visible', false)
+ },`,
+ handelConfirm: `handelConfirm() {
+ this.$refs['${confGlobal.formRef}'].validate(valid => {
+ if(!valid) return
+ this.close()
+ })
+ },`
+ }
+ }
+
+ const methods = minxins[type]
+ if (methods) {
+ Object.keys(methods).forEach(key => {
+ list.push(methods[key])
+ })
+ }
+
+ return list
+}
+
+function buildData(conf, dataList) {
+ if (conf.vModel === undefined) return
+ let defaultValue
+ if (typeof (conf.defaultValue) === 'string' && !conf.multiple) {
+ defaultValue = `'${conf.defaultValue}'`
+ } else {
+ defaultValue = `${JSON.stringify(conf.defaultValue)}`
+ }
+ dataList.push(`${conf.vModel}: ${defaultValue},`)
+}
+
+function buildRules(conf, ruleList) {
+ if (conf.vModel === undefined) return
+ const rules = []
+ if (trigger[conf.tag]) {
+ if (conf.required) {
+ const type = isArray(conf.defaultValue) ? 'type: \'array\',' : ''
+ let message = isArray(conf.defaultValue) ? `请至少选择一个${conf.vModel}` : conf.placeholder
+ if (message === undefined) message = `${conf.label}不能为空`
+ rules.push(`{ required: true, ${type} message: '${message}', trigger: '${trigger[conf.tag]}' }`)
+ }
+ if (conf.regList && isArray(conf.regList)) {
+ conf.regList.forEach(item => {
+ if (item.pattern) {
+ rules.push(`{ pattern: ${eval(item.pattern)}, message: '${item.message}', trigger: '${trigger[conf.tag]}' }`)
+ }
+ })
+ }
+ ruleList.push(`${conf.vModel}: [${rules.join(',')}],`)
+ }
+}
+
+function buildOptions(conf, optionsList) {
+ if (conf.vModel === undefined) return
+ if (conf.dataType === 'dynamic') { conf.options = [] }
+ const str = `${conf.vModel}Options: ${JSON.stringify(conf.options)},`
+ optionsList.push(str)
+}
+
+function buildProps(conf, propsList) {
+ if (conf.dataType === 'dynamic') {
+ conf.valueKey !== 'value' && (conf.props.props.value = conf.valueKey)
+ conf.labelKey !== 'label' && (conf.props.props.label = conf.labelKey)
+ conf.childrenKey !== 'children' && (conf.props.props.children = conf.childrenKey)
+ }
+ const str = `${conf.vModel}Props: ${JSON.stringify(conf.props.props)},`
+ propsList.push(str)
+}
+
+function buildBeforeUpload(conf) {
+ const unitNum = units[conf.sizeUnit]; let rightSizeCode = ''; let acceptCode = ''; const
+ returnList = []
+ if (conf.fileSize) {
+ rightSizeCode = `let isRightSize = file.size / ${unitNum} < ${conf.fileSize}
+ if(!isRightSize){
+ this.$message.error('文件大小超过 ${conf.fileSize}${conf.sizeUnit}')
+ }`
+ returnList.push('isRightSize')
+ }
+ if (conf.accept) {
+ acceptCode = `let isAccept = new RegExp('${conf.accept}').test(file.type)
+ if(!isAccept){
+ this.$message.error('应该选择${conf.accept}类型的文件')
+ }`
+ returnList.push('isAccept')
+ }
+ const str = `${conf.vModel}BeforeUpload(file) {
+ ${rightSizeCode}
+ ${acceptCode}
+ return ${returnList.join('&&')}
+ },`
+ return returnList.length ? str : ''
+}
+
+function buildSubmitUpload(conf) {
+ const str = `submitUpload() {
+ this.$refs['${conf.vModel}'].submit()
+ },`
+ return str
+}
+
+function buildOptionMethod(methodName, model, methodList) {
+ const str = `${methodName}() {
+ // TODO 发起请求获取数据
+ this.${model}
+ },`
+ methodList.push(str)
+}
+
+function buildexport(conf, type, data, rules, selectOptions, uploadVar, props, methods) {
+ const str = `${exportDefault}{
+ ${inheritAttrs[type]}
+ components: {},
+ props: [],
+ data () {
+ return {
+ ${conf.formModel}: {
+ ${data}
+ },
+ ${conf.formRules}: {
+ ${rules}
+ },
+ ${uploadVar}
+ ${selectOptions}
+ ${props}
+ }
+ },
+ computed: {},
+ watch: {},
+ created () {},
+ mounted () {},
+ methods: {
+ ${methods}
+ }
+}`
+ return str
+}
diff --git a/ruoyi-ui/src/utils/generator/render.js b/ruoyi-ui/src/utils/generator/render.js
new file mode 100644
index 0000000..42cd664
--- /dev/null
+++ b/ruoyi-ui/src/utils/generator/render.js
@@ -0,0 +1,121 @@
+import { makeMap } from '@/utils/index'
+
+// 参考https://github.com/vuejs/vue/blob/v2.6.10/src/platforms/web/server/util.js
+const isAttr = makeMap(
+ 'accept,accept-charset,accesskey,action,align,alt,async,autocomplete,'
+ + 'autofocus,autoplay,autosave,bgcolor,border,buffered,challenge,charset,'
+ + 'checked,cite,class,code,codebase,color,cols,colspan,content,http-equiv,'
+ + 'name,contenteditable,contextmenu,controls,coords,data,datetime,default,'
+ + 'defer,dir,dirname,disabled,download,draggable,dropzone,enctype,method,for,'
+ + 'form,formaction,headers,height,hidden,high,href,hreflang,http-equiv,'
+ + 'icon,id,ismap,itemprop,keytype,kind,label,lang,language,list,loop,low,'
+ + 'manifest,max,maxlength,media,method,GET,POST,min,multiple,email,file,'
+ + 'muted,name,novalidate,open,optimum,pattern,ping,placeholder,poster,'
+ + 'preload,radiogroup,readonly,rel,required,reversed,rows,rowspan,sandbox,'
+ + 'scope,scoped,seamless,selected,shape,size,type,text,password,sizes,span,'
+ + 'spellcheck,src,srcdoc,srclang,srcset,start,step,style,summary,tabindex,'
+ + 'target,title,type,usemap,value,width,wrap'
+)
+
+function vModel(self, dataObject, defaultValue) {
+ dataObject.props.value = defaultValue
+
+ dataObject.on.input = val => {
+ self.$emit('input', val)
+ }
+}
+
+const componentChild = {
+ 'el-input': {
+ prepend(h, conf, key) {
+ return <template slot="prepend">{conf[key]}</template>
+ },
+ append(h, conf, key) {
+ return <template slot="append">{conf[key]}</template>
+ }
+ },
+ 'el-select': {
+ options(h, conf, key) {
+ const list = []
+ conf.options.forEach(item => {
+ list.push(<el-option label={item.label} value={item.value} disabled={item.disabled}></el-option>)
+ })
+ return list
+ }
+ },
+ 'el-radio-group': {
+ options(h, conf, key) {
+ const list = []
+ conf.options.forEach(item => {
+ if (conf.optionType === 'button') list.push(<el-radio-button label={item.value}>{item.label}</el-radio-button>)
+ else list.push(<el-radio label={item.value} border={conf.border}>{item.label}</el-radio>)
+ })
+ return list
+ }
+ },
+ 'el-checkbox-group': {
+ options(h, conf, key) {
+ const list = []
+ conf.options.forEach(item => {
+ if (conf.optionType === 'button') {
+ list.push(<el-checkbox-button label={item.value}>{item.label}</el-checkbox-button>)
+ } else {
+ list.push(<el-checkbox label={item.value} border={conf.border}>{item.label}</el-checkbox>)
+ }
+ })
+ return list
+ }
+ },
+ 'el-upload': {
+ 'list-type': (h, conf, key) => {
+ const list = []
+ if (conf['list-type'] === 'picture-card') {
+ list.push(<i class="el-icon-plus"></i>)
+ } else {
+ list.push(<el-button size="small" type="primary" icon="el-icon-upload">{conf.buttonText}</el-button>)
+ }
+ if (conf.showTip) {
+ list.push(<div slot="tip" class="el-upload__tip">只能上传不超过 {conf.fileSize}{conf.sizeUnit} 的{conf.accept}文件</div>)
+ }
+ return list
+ }
+ }
+}
+
+export default {
+ render(h) {
+ const dataObject = {
+ attrs: {},
+ props: {},
+ on: {},
+ style: {}
+ }
+ const confClone = JSON.parse(JSON.stringify(this.conf))
+ const children = []
+
+ const childObjs = componentChild[confClone.tag]
+ if (childObjs) {
+ Object.keys(childObjs).forEach(key => {
+ const childFunc = childObjs[key]
+ if (confClone[key]) {
+ children.push(childFunc(h, confClone, key))
+ }
+ })
+ }
+
+ Object.keys(confClone).forEach(key => {
+ const val = confClone[key]
+ if (key === 'vModel') {
+ vModel(this, dataObject, confClone.defaultValue)
+ } else if (dataObject[key]) {
+ dataObject[key] = val
+ } else if (!isAttr(key)) {
+ dataObject.props[key] = val
+ } else {
+ dataObject.attrs[key] = val
+ }
+ })
+ return h(this.conf.tag, dataObject, children)
+ },
+ props: ['conf']
+}
diff --git a/ruoyi-ui/src/utils/index.js b/ruoyi-ui/src/utils/index.js
new file mode 100644
index 0000000..6c3017c
--- /dev/null
+++ b/ruoyi-ui/src/utils/index.js
@@ -0,0 +1,386 @@
+/**
+ * 表格时间格式化
+ */
+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, ' ')
+ }
+}
+
+export function makeMap(str, expectsLowerCase) {
+ const map = Object.create(null)
+ const list = str.split(',')
+ for (let i = 0; i < list.length; i++) {
+ map[list[i]] = true
+ }
+ return expectsLowerCase
+ ? val => map[val.toLowerCase()]
+ : val => map[val]
+}
+
+export const exportDefault = 'export default '
+
+export const beautifierConf = {
+ html: {
+ indent_size: '2',
+ indent_char: ' ',
+ max_preserve_newlines: '-1',
+ preserve_newlines: false,
+ keep_array_indentation: false,
+ break_chained_methods: false,
+ indent_scripts: 'separate',
+ brace_style: 'end-expand',
+ space_before_conditional: true,
+ unescape_strings: false,
+ jslint_happy: false,
+ end_with_newline: true,
+ wrap_line_length: '110',
+ indent_inner_html: true,
+ comma_first: false,
+ e4x: true,
+ indent_empty_lines: true
+ },
+ js: {
+ indent_size: '2',
+ indent_char: ' ',
+ max_preserve_newlines: '-1',
+ preserve_newlines: false,
+ keep_array_indentation: false,
+ break_chained_methods: false,
+ indent_scripts: 'normal',
+ brace_style: 'end-expand',
+ space_before_conditional: true,
+ unescape_strings: false,
+ jslint_happy: true,
+ end_with_newline: true,
+ wrap_line_length: '110',
+ indent_inner_html: true,
+ comma_first: false,
+ e4x: true,
+ indent_empty_lines: true
+ }
+}
+
+// 首字母大小
+export function titleCase(str) {
+ return str.replace(/( |^)[a-z]/g, L => L.toUpperCase())
+}
+
+// 下划转驼峰
+export function camelCase(str) {
+ return str.replace(/-[a-z]/g, str1 => str1.substr(-1).toUpperCase())
+}
+
+export function isNumberStr(str) {
+ return /^[+-]?(0|([1-9]\d*))(\.\d+)?$/g.test(str)
+}
+
\ No newline at end of file
diff --git a/ruoyi-ui/src/utils/jsencrypt.js b/ruoyi-ui/src/utils/jsencrypt.js
new file mode 100644
index 0000000..3171a8f
--- /dev/null
+++ b/ruoyi-ui/src/utils/jsencrypt.js
@@ -0,0 +1,30 @@
+import JSEncrypt from 'jsencrypt/bin/jsencrypt'
+
+// 密钥对生成 http://web.chacuo.net/netrsakeypair
+
+const publicKey = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANL378k3RiZHWx5AfJqdH9xRNBmD9wGD\n' +
+ '2iRe41HdTNF8RUhNnHit5NpMNtGL0NPTSSpPjjI1kJfVorRvaQerUgkCAwEAAQ=='
+
+const privateKey = 'MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEA0vfvyTdGJkdbHkB8\n' +
+ 'mp0f3FE0GYP3AYPaJF7jUd1M0XxFSE2ceK3k2kw20YvQ09NJKk+OMjWQl9WitG9p\n' +
+ 'B6tSCQIDAQABAkA2SimBrWC2/wvauBuYqjCFwLvYiRYqZKThUS3MZlebXJiLB+Ue\n' +
+ '/gUifAAKIg1avttUZsHBHrop4qfJCwAI0+YRAiEA+W3NK/RaXtnRqmoUUkb59zsZ\n' +
+ 'UBLpvZgQPfj1MhyHDz0CIQDYhsAhPJ3mgS64NbUZmGWuuNKp5coY2GIj/zYDMJp6\n' +
+ 'vQIgUueLFXv/eZ1ekgz2Oi67MNCk5jeTF2BurZqNLR3MSmUCIFT3Q6uHMtsB9Eha\n' +
+ '4u7hS31tj1UWE+D+ADzp59MGnoftAiBeHT7gDMuqeJHPL4b+kC+gzV4FGTfhR9q3\n' +
+ 'tTbklZkD2A=='
+
+// 加密
+export function encrypt(txt) {
+ const encryptor = new JSEncrypt()
+ encryptor.setPublicKey(publicKey) // 设置公钥
+ return encryptor.encrypt(txt) // 对需要加密的数据进行加密
+}
+
+// 解密
+export function decrypt(txt) {
+ const encryptor = new JSEncrypt()
+ encryptor.setPrivateKey(privateKey)
+ return encryptor.decrypt(txt)
+}
+
diff --git a/ruoyi-ui/src/utils/permission.js b/ruoyi-ui/src/utils/permission.js
new file mode 100644
index 0000000..784e379
--- /dev/null
+++ b/ruoyi-ui/src/utils/permission.js
@@ -0,0 +1,49 @@
+import store from '@/store'
+
+/**
+ * 字符权限校验
+ * @param {Array} value 校验值
+ * @returns {Boolean}
+ */
+export function checkPermi(value) {
+ if (value && value instanceof Array && value.length > 0) {
+ const permissions = store.getters && store.getters.permissions
+ const permissionDatas = value
+
+ const hasPermission = permissions.some(permission => {
+ return permissionDatas.includes(permission)
+ })
+
+ if (!hasPermission) {
+ return false
+ }
+ return true
+ } else {
+ console.error(`need roles! Like checkPermi="['system:user:add','system:user:edit']"`)
+ return false
+ }
+}
+
+/**
+ * 角色权限校验
+ * @param {Array} value 校验值
+ * @returns {Boolean}
+ */
+export function checkRole(value) {
+ if (value && value instanceof Array && value.length > 0) {
+ const roles = store.getters && store.getters.roles
+ const permissionRoles = value
+
+ const hasRole = roles.some(role => {
+ return permissionRoles.includes(role)
+ })
+
+ if (!hasRole) {
+ return false
+ }
+ return true
+ } else {
+ console.error(`need roles! Like checkRole="['admin','editor']"`)
+ return false
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-ui/src/utils/request.js b/ruoyi-ui/src/utils/request.js
new file mode 100644
index 0000000..5d1cfd3
--- /dev/null
+++ b/ruoyi-ui/src/utils/request.js
@@ -0,0 +1,102 @@
+import axios from 'axios'
+import { Notification, MessageBox, Message } from 'element-ui'
+import store from '@/store'
+import { getToken } from '@/utils/auth'
+import errorCode from '@/utils/errorCode'
+import { tansParams } from "@/utils/ruoyi";
+
+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 => {
+ const isToken = (config.headers || {}).isToken === false
+ if (getToken() && !isToken) {
+ 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 || 200;
+ const message = errorCode[code] || res.data.msg || errorCode['default']
+ if (code === 401) {
+ MessageBox.confirm(
+ '登录状态已过期,您可以继续留在该页面,或者重新登录',
+ '系统提示',
+ {
+ confirmButtonText: '重新登录',
+ cancelButtonText: '取消',
+ type: 'warning'
+ }
+ ).then(() => {
+ store.dispatch('LogOut').then(() => {
+ location.reload() // 为了重新实例化vue-router对象 避免bug
+ })
+ })
+ } else if (code === 500) {
+ Message({
+ message: message,
+ type: 'error'
+ })
+ return Promise.reject(new Error(message))
+ } else if (code !== 200) {
+ Notification.error({
+ title: message
+ })
+ 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 function download(url, params, filename) {
+ return service.post(url, params, {
+ transformRequest: [(params) => {
+ return tansParams(params)
+ }],
+ responseType: 'blob'
+ }).then((data) => {
+ const content = data
+ const blob = new Blob([content])
+ if ('download' in document.createElement('a')) {
+ const elink = document.createElement('a')
+ elink.download = filename
+ elink.style.display = 'none'
+ elink.href = URL.createObjectURL(blob)
+ document.body.appendChild(elink)
+ elink.click()
+ URL.revokeObjectURL(elink.href)
+ document.body.removeChild(elink)
+ } else {
+ navigator.msSaveBlob(blob, filename)
+ }
+ }).catch((r) => {
+ console.error(r)
+ })
+}
+
+
+
+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..f834dda
--- /dev/null
+++ b/ruoyi-ui/src/utils/ruoyi.js
@@ -0,0 +1,147 @@
+/**
+ * 通用js方法封装处理
+ * Copyright (c) 2019 ruoyi
+ */
+
+const baseURL = process.env.VUE_APP_BASE_API
+
+// 日期格式化
+export function parseTime(time, pattern) {
+ if (arguments.length === 0 || !time) {
+ return null
+ }
+ const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}'
+ let date
+ if (typeof time === 'object') {
+ date = time
+ } else {
+ if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
+ time = parseInt(time)
+ } else if (typeof time === 'string') {
+ time = time.replace(new RegExp(/-/gm), '/');
+ }
+ if ((typeof time === 'number') && (time.toString().length === 10)) {
+ time = time * 1000
+ }
+ date = new Date(time)
+ }
+ const formatObj = {
+ y: date.getFullYear(),
+ m: date.getMonth() + 1,
+ d: date.getDate(),
+ h: date.getHours(),
+ i: date.getMinutes(),
+ s: date.getSeconds(),
+ a: date.getDay()
+ }
+ const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
+ let value = formatObj[key]
+ // Note: getDay() returns 0 on Sunday
+ if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] }
+ if (result.length > 0 && value < 10) {
+ value = '0' + value
+ }
+ return value || 0
+ })
+ return time_str
+}
+
+// 表单重置
+export function resetForm(refName) {
+ if (this.$refs[refName]) {
+ this.$refs[refName].resetFields();
+ }
+}
+
+// 添加日期范围
+export function addDateRange(params, dateRange) {
+ var search = params;
+ search.beginTime = "";
+ search.endTime = "";
+ if (null != dateRange && '' != dateRange) {
+ search.beginTime = this.dateRange[0];
+ search.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('');
+}
+
+// 通用下载方法
+export function download(fileName) {
+ window.location.href = baseURL + "/common/download?fileName=" + encodeURI(fileName) + "&delete=" + true;
+}
+
+// 字符串格式化(%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 : '';
+}
+
+// 转换字符串,undefined,null等转化为""
+export function praseStrEmpty(str) {
+ if (!str || str == "undefined" || str == "null") {
+ return "";
+ }
+ return str;
+}
+
+/**
+ * 构造树型结构数据
+ * @param {*} data 数据源
+ * @param {*} id id字段 默认 'id'
+ * @param {*} parentId 父节点字段 默认 'parentId'
+ * @param {*} children 孩子节点字段 默认 'children'
+ * @param {*} rootId 根Id 默认 0
+ */
+export function handleTree(data, id, parentId, children, rootId) {
+ id = id || 'id'
+ parentId = parentId || 'parentId'
+ children = children || 'children'
+ rootId = rootId || 0
+ //对源数据深度克隆
+ const cloneData = JSON.parse(JSON.stringify(data))
+ //循环所有项
+ const treeData = cloneData.filter(father => {
+ let branchArr = cloneData.filter(child => {
+ //返回每一项的子级数组
+ return father[id] === child[parentId]
+ });
+ branchArr.length > 0 ? father.children = branchArr : '';
+ //返回第一层
+ return father[parentId] === rootId;
+ });
+ return treeData != '' ? treeData : data;
+ }
+
+ /**
+ * 参数处理
+ * @param {*} params 参数
+ */
+export function tansParams(params) {
+ let result = ''
+ Object.keys(params).forEach((key) => {
+ if (!Object.is(params[key], undefined) && !Object.is(params[key], null)) {
+ result += encodeURIComponent(key) + '=' + encodeURIComponent(params[key]) + '&'
+ }
+ })
+ return result
+}
\ 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/utils/zipdownload.js b/ruoyi-ui/src/utils/zipdownload.js
new file mode 100644
index 0000000..3a86814
--- /dev/null
+++ b/ruoyi-ui/src/utils/zipdownload.js
@@ -0,0 +1,40 @@
+import axios from 'axios'
+import { getToken } from '@/utils/auth'
+
+const mimeMap = {
+ xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+ zip: 'application/zip'
+}
+
+const baseUrl = process.env.VUE_APP_BASE_API
+export function downLoadZip(str, filename) {
+ var url = baseUrl + str
+ axios({
+ method: 'get',
+ url: url,
+ responseType: 'blob',
+ headers: { 'Authorization': 'Bearer ' + getToken() }
+ }).then(res => {
+ resolveBlob(res, mimeMap.zip)
+ })
+}
+/**
+ * 解析blob响应内容并下载
+ * @param {*} res blob响应内容
+ * @param {String} mimeType MIME类型
+ */
+export function resolveBlob(res, mimeType) {
+ const aLink = document.createElement('a')
+ var blob = new Blob([res.data], { type: mimeType })
+ // //从response的headers中获取filename, 后端response.setHeader("Content-disposition", "attachment; filename=xxxx.docx") 设置的文件名;
+ var patt = new RegExp('filename=([^;]+\\.[^\\.;]+);*')
+ var contentDisposition = decodeURI(res.headers['content-disposition'])
+ var result = patt.exec(contentDisposition)
+ var fileName = result[1]
+ fileName = fileName.replace(/\"/g, '')
+ aLink.href = URL.createObjectURL(blob)
+ aLink.setAttribute('download', fileName) // 设置下载文件名称
+ document.body.appendChild(aLink)
+ aLink.click()
+ document.body.appendChild(aLink)
+}
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..ae33ba0
--- /dev/null
+++ b/ruoyi-ui/src/views/components/icons/element-icons.js
@@ -0,0 +1,3 @@
+const elementIcons = ['platform-eleme', 'eleme', 'delete-solid', 'delete', 's-tools', 'setting', 'user-solid', 'user', 'phone', 'phone-outline', 'more', 'more-outline', 'star-on', 'star-off', 's-goods', 'goods', 'warning', 'warning-outline', 'question', 'info', 'remove', 'circle-plus', 'success', 'error', 'zoom-in', 'zoom-out', 'remove-outline', 'circle-plus-outline', 'circle-check', 'circle-close', 's-help', 'help', 'minus', 'plus', 'check', 'close', 'picture', 'picture-outline', 'picture-outline-round', 'upload', 'upload2', 'download', 'camera-solid', 'camera', 'video-camera-solid', 'video-camera', 'message-solid', 'bell', 's-cooperation', 's-order', 's-platform', 's-fold', 's-unfold', 's-operation', 's-promotion', 's-home', 's-release', 's-ticket', 's-management', 's-open', 's-shop', 's-marketing', 's-flag', 's-comment', 's-finance', 's-claim', 's-custom', 's-opportunity', 's-data', 's-check', 's-grid', 'menu', 'share', 'd-caret', 'caret-left', 'caret-right', 'caret-bottom', 'caret-top', 'bottom-left', 'bottom-right', 'back', 'right', 'bottom', 'top', 'top-left', 'top-right', 'arrow-left', 'arrow-right', 'arrow-down', 'arrow-up', 'd-arrow-left', 'd-arrow-right', 'video-pause', 'video-play', 'refresh', 'refresh-right', 'refresh-left', 'finished', 'sort', 'sort-up', 'sort-down', 'rank', 'loading', 'view', 'c-scale-to-original', 'date', 'edit', 'edit-outline', 'folder', 'folder-opened', 'folder-add', 'folder-remove', 'folder-delete', 'folder-checked', 'tickets', 'document-remove', 'document-delete', 'document-copy', 'document-checked', 'document', 'document-add', 'printer', 'paperclip', 'takeaway-box', 'search', 'monitor', 'attract', 'mobile', 'scissors', 'umbrella', 'headset', 'brush', 'mouse', 'coordinate', 'magic-stick', 'reading', 'data-line', 'data-board', 'pie-chart', 'data-analysis', 'collection-tag', 'film', 'suitcase', 'suitcase-1', 'receiving', 'collection', 'files', 'notebook-1', 'notebook-2', 'toilet-paper', 'office-building', 'school', 'table-lamp', 'house', 'no-smoking', 'smoking', 'shopping-cart-full', 'shopping-cart-1', 'shopping-cart-2', 'shopping-bag-1', 'shopping-bag-2', 'sold-out', 'sell', 'present', 'box', 'bank-card', 'money', 'coin', 'wallet', 'discount', 'price-tag', 'news', 'guide', 'male', 'female', 'thumb', 'cpu', 'link', 'connection', 'open', 'turn-off', 'set-up', 'chat-round', 'chat-line-round', 'chat-square', 'chat-dot-round', 'chat-dot-square', 'chat-line-square', 'message', 'postcard', 'position', 'turn-off-microphone', 'microphone', 'close-notification', 'bangzhu', 'time', 'odometer', 'crop', 'aim', 'switch-button', 'full-screen', 'copy-document', 'mic', 'stopwatch', 'medal-1', 'medal', 'trophy', 'trophy-1', 'first-aid-kit', 'discover', 'place', 'location', 'location-outline', 'location-information', 'add-location', 'delete-location', 'map-location', 'alarm-clock', 'timer', 'watch-1', 'watch', 'lock', 'unlock', 'key', 'service', 'mobile-phone', 'bicycle', 'truck', 'ship', 'basketball', 'football', 'soccer', 'baseball', 'wind-power', 'light-rain', 'lightning', 'heavy-rain', 'sunrise', 'sunrise-1', 'sunset', 'sunny', 'cloudy', 'partly-cloudy', 'cloudy-and-sunny', 'moon', 'moon-night', 'dish', 'dish-1', 'food', 'chicken', 'fork-spoon', 'knife-fork', 'burger', 'tableware', 'sugar', 'dessert', 'ice-cream', 'hot-water', 'water-cup', 'coffee-cup', 'cold-drink', 'goblet', 'goblet-full', 'goblet-square', 'goblet-square-full', 'refrigerator', 'grape', 'watermelon', 'cherry', 'apple', 'pear', 'orange', 'coffee', 'ice-tea', 'ice-drink', 'milk-tea', 'potato-strips', 'lollipop', 'ice-cream-square', 'ice-cream-round']
+
+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..d26194c
--- /dev/null
+++ b/ruoyi-ui/src/views/dashboard/mixins/resize.js
@@ -0,0 +1,56 @@
+import { debounce } from '@/utils'
+
+export default {
+ data() {
+ return {
+ $_sidebarElm: null,
+ $_resizeHandler: null
+ }
+ },
+ mounted() {
+ this.initListener()
+ },
+ activated() {
+ if (!this.$_resizeHandler) {
+ // avoid duplication init
+ this.initListener()
+ }
+
+ // when keep-alive chart activated, auto resize
+ this.resize()
+ },
+ beforeDestroy() {
+ this.destroyListener()
+ },
+ deactivated() {
+ this.destroyListener()
+ },
+ methods: {
+ // use $_ for mixins properties
+ // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential
+ $_sidebarResizeHandler(e) {
+ if (e.propertyName === 'width') {
+ this.$_resizeHandler()
+ }
+ },
+ initListener() {
+ this.$_resizeHandler = debounce(() => {
+ this.resize()
+ }, 100)
+ window.addEventListener('resize', this.$_resizeHandler)
+
+ this.$_sidebarElm = document.getElementsByClassName('sidebar-container')[0]
+ this.$_sidebarElm && this.$_sidebarElm.addEventListener('transitionend', this.$_sidebarResizeHandler)
+ },
+ destroyListener() {
+ window.removeEventListener('resize', this.$_resizeHandler)
+ this.$_resizeHandler = null
+
+ this.$_sidebarElm && this.$_sidebarElm.removeEventListener('transitionend', this.$_sidebarResizeHandler)
+ },
+ resize() {
+ const { chart } = this
+ chart && chart.resize()
+ }
+ }
+}
diff --git a/ruoyi-ui/src/views/error/401.vue b/ruoyi-ui/src/views/error/401.vue
new file mode 100644
index 0000000..9664fba
--- /dev/null
+++ b/ruoyi-ui/src/views/error/401.vue
@@ -0,0 +1,88 @@
+<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">
+ 401错误!
+ </h1>
+ <h2>您没有访问权限!</h2>
+ <h6>对不起,您没有访问权限,请不要进行非法操作!您可以返回主页面</h6>
+ <ul class="list-unstyled">
+ <li class="link-type">
+ <router-link to="/">
+ 回首页
+ </router-link>
+ </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>
+ </div>
+</template>
+
+<script>
+import errGif from '@/assets/401_images/401.gif'
+
+export default {
+ name: 'Page401',
+ data() {
+ return {
+ errGif: errGif + '?' + +new Date()
+ }
+ },
+ methods: {
+ back() {
+ if (this.$route.query.noGoBack) {
+ this.$router.push({ path: '/' })
+ } 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..666d27c
--- /dev/null
+++ b/ruoyi-ui/src/views/error/404.vue
@@ -0,0 +1,233 @@
+<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">
+ 404错误!
+ </div>
+ <div class="bullshit__headline">
+ {{ message }}
+ </div>
+ <div class="bullshit__info">
+ 对不起,您正在寻找的页面不存在。尝试检查URL的错误,然后按浏览器上的刷新按钮或尝试在我们的应用程序中找到其他内容。
+ </div>
+ <router-link to="/" class="bullshit__return-home">
+ 返回首页
+ </router-link>
+ </div>
+ </div>
+ </div>
+</template>
+
+<script>
+
+export default {
+ name: 'Page404',
+ computed: {
+ message() {
+ return '找不到网页!'
+ }
+ }
+}
+</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..4f0bf5e
--- /dev/null
+++ b/ruoyi-ui/src/views/login.vue
@@ -0,0 +1,206 @@
+<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" class="login-code-img"/>
+ </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";
+import { encrypt, decrypt } from '@/utils/jsencrypt'
+
+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 : decrypt(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", encrypt(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.$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;
+}
+.login-code-img {
+ height: 40px;
+}
+</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..9e8e1e7
--- /dev/null
+++ b/ruoyi-ui/src/views/monitor/druid/index.vue
@@ -0,0 +1,26 @@
+<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 {
+ name: "Druid",
+ 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..3c8e73f
--- /dev/null
+++ b/ruoyi-ui/src/views/monitor/job/index.vue
@@ -0,0 +1,481 @@
+<template>
+ <div class="app-container">
+ <el-form :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
+ <el-form-item label="任务名称" prop="jobName">
+ <el-input
+ v-model="queryParams.jobName"
+ placeholder="请输入任务名称"
+ clearable
+ size="small"
+ @keyup.enter.native="handleQuery"
+ />
+ </el-form-item>
+ <el-form-item label="任务组名" prop="jobGroup">
+ <el-select v-model="queryParams.jobGroup" placeholder="请选择任务组名" clearable size="small">
+ <el-option
+ v-for="dict in jobGroupOptions"
+ :key="dict.dictValue"
+ :label="dict.dictLabel"
+ :value="dict.dictValue"
+ />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="任务状态" prop="status">
+ <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 icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+ </el-form-item>
+ </el-form>
+
+ <el-row :gutter="10" class="mb8">
+ <el-col :span="1.5">
+ <el-button
+ type="primary"
+ icon="el-icon-plus"
+ size="mini"
+ @click="handleAdd"
+ v-hasPermi="['monitor:job:add']"
+ >新增</el-button>
+ </el-col>
+ <el-col :span="1.5">
+ <el-button
+ type="success"
+ icon="el-icon-edit"
+ size="mini"
+ :disabled="single"
+ @click="handleUpdate"
+ v-hasPermi="['monitor:job:edit']"
+ >修改</el-button>
+ </el-col>
+ <el-col :span="1.5">
+ <el-button
+ type="danger"
+ icon="el-icon-delete"
+ size="mini"
+ :disabled="multiple"
+ @click="handleDelete"
+ v-hasPermi="['monitor:job:remove']"
+ >删除</el-button>
+ </el-col>
+ <el-col :span="1.5">
+ <el-button
+ type="warning"
+ icon="el-icon-download"
+ size="mini"
+ @click="handleExport"
+ v-hasPermi="['monitor:job:export']"
+ >导出</el-button>
+ </el-col>
+ <el-col :span="1.5">
+ <el-button
+ type="info"
+ icon="el-icon-s-operation"
+ size="mini"
+ @click="handleJobLog"
+ v-hasPermi="['monitor:job:query']"
+ >日志</el-button>
+ </el-col>
+ </el-row>
+
+ <el-table v-loading="loading" :data="jobList" @selection-change="handleSelectionChange">
+ <el-table-column type="selection" width="55" align="center" />
+ <el-table-column label="任务编号" align="center" prop="jobId" />
+ <el-table-column label="任务名称" align="center" prop="jobName" :show-overflow-tooltip="true" />
+ <el-table-column label="任务组名" align="center" prop="jobGroup" :formatter="jobGroupFormat" />
+ <el-table-column label="调用目标字符串" align="center" prop="invokeTarget" :show-overflow-tooltip="true" />
+ <el-table-column label="cron执行表达式" align="center" prop="cronExpression" :show-overflow-tooltip="true" />
+ <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" class-name="small-padding fixed-width">
+ <template slot-scope="scope">
+ <el-button
+ size="mini"
+ type="text"
+ icon="el-icon-caret-right"
+ @click="handleRun(scope.row)"
+ v-hasPermi="['monitor:job:edit']"
+ >执行一次</el-button>
+ <el-button
+ size="mini"
+ type="text"
+ icon="el-icon-view"
+ @click="handleView(scope.row)"
+ v-hasPermi="['monitor:job: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="title" :visible.sync="open" width="700px" append-to-body>
+ <el-form ref="form" :model="form" :rules="rules" label-width="100px">
+ <el-row>
+ <el-col :span="12">
+ <el-form-item label="任务名称" prop="jobName">
+ <el-input v-model="form.jobName" placeholder="请输入任务名称" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="任务分组" prop="jobGroup">
+ <el-select v-model="form.jobGroup" placeholder="请选择">
+ <el-option
+ v-for="dict in jobGroupOptions"
+ :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 prop="invokeTarget">
+ <span slot="label">
+ 调用方法
+ <el-tooltip placement="top">
+ <div slot="content">
+ Bean调用示例:ryTask.ryParams('ry')
+ <br />Class类调用示例:com.ruoyi.quartz.task.RyTask.ryParams('ry')
+ <br />参数说明:支持字符串,布尔类型,长整型,浮点型,整型
+ </div>
+ <i class="el-icon-question"></i>
+ </el-tooltip>
+ </span>
+ <el-input v-model="form.invokeTarget" placeholder="请输入调用目标字符串" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="cron表达式" prop="cronExpression">
+ <el-input v-model="form.cronExpression" placeholder="请输入cron执行表达式" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="是否并发" prop="concurrent">
+ <el-radio-group v-model="form.concurrent" size="small">
+ <el-radio-button label="0">允许</el-radio-button>
+ <el-radio-button label="1">禁止</el-radio-button>
+ </el-radio-group>
+ </el-form-item>
+ </el-col>
+ <el-col :span="24">
+ <el-form-item label="错误策略" prop="misfirePolicy">
+ <el-radio-group v-model="form.misfirePolicy" size="small">
+ <el-radio-button label="1">立即执行</el-radio-button>
+ <el-radio-button label="2">执行一次</el-radio-button>
+ <el-radio-button label="3">放弃执行</el-radio-button>
+ </el-radio-group>
+ </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-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>
+
+ <!-- 任务日志详细 -->
+ <el-dialog title="任务详细" :visible.sync="openView" width="700px" append-to-body>
+ <el-form ref="form" :model="form" label-width="120px" size="mini">
+ <el-row>
+ <el-col :span="12">
+ <el-form-item label="任务编号:">{{ form.jobId }}</el-form-item>
+ <el-form-item label="任务名称:">{{ form.jobName }}</el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="任务分组:">{{ jobGroupFormat(form) }}</el-form-item>
+ <el-form-item label="创建时间:">{{ form.createTime }}</el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="cron表达式:">{{ form.cronExpression }}</el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="下次执行时间:">{{ parseTime(form.nextValidTime) }}</el-form-item>
+ </el-col>
+ <el-col :span="24">
+ <el-form-item label="调用目标方法:">{{ form.invokeTarget }}</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="是否并发:">
+ <div v-if="form.concurrent == 0">允许</div>
+ <div v-else-if="form.concurrent == 1">禁止</div>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="执行策略:">
+ <div v-if="form.misfirePolicy == 0">默认策略</div>
+ <div v-else-if="form.misfirePolicy == 1">立即执行</div>
+ <div v-else-if="form.misfirePolicy == 2">执行一次</div>
+ <div v-else-if="form.misfirePolicy == 3">放弃执行</div>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ </el-form>
+ <div slot="footer" class="dialog-footer">
+ <el-button @click="openView = false">关 闭</el-button>
+ </div>
+ </el-dialog>
+ </div>
+</template>
+
+<script>
+import { listJob, getJob, delJob, addJob, updateJob, runJob, changeJobStatus } from "@/api/monitor/job";
+
+export default {
+ name: "Job",
+ data() {
+ return {
+ // 遮罩层
+ loading: true,
+ // 选中数组
+ ids: [],
+ // 非单个禁用
+ single: true,
+ // 非多个禁用
+ multiple: true,
+ // 总条数
+ total: 0,
+ // 定时任务表格数据
+ jobList: [],
+ // 弹出层标题
+ title: "",
+ // 是否显示弹出层
+ open: false,
+ // 是否显示详细弹出层
+ openView: false,
+ // 任务组名字典
+ jobGroupOptions: [],
+ // 状态字典
+ statusOptions: [],
+ // 查询参数
+ queryParams: {
+ pageNum: 1,
+ pageSize: 10,
+ jobName: undefined,
+ jobGroup: undefined,
+ status: undefined
+ },
+ // 表单参数
+ form: {},
+ // 表单校验
+ rules: {
+ jobName: [
+ { required: true, message: "任务名称不能为空", trigger: "blur" }
+ ],
+ invokeTarget: [
+ { required: true, message: "调用目标字符串不能为空", trigger: "blur" }
+ ],
+ cronExpression: [
+ { required: true, message: "cron执行表达式不能为空", trigger: "blur" }
+ ]
+ }
+ };
+ },
+ created() {
+ this.getList();
+ this.getDicts("sys_job_group").then(response => {
+ this.jobGroupOptions = response.data;
+ });
+ this.getDicts("sys_job_status").then(response => {
+ this.statusOptions = response.data;
+ });
+ },
+ methods: {
+ /** 查询定时任务列表 */
+ getList() {
+ this.loading = true;
+ listJob(this.queryParams).then(response => {
+ this.jobList = response.rows;
+ this.total = response.total;
+ this.loading = false;
+ });
+ },
+ // 任务组名字典翻译
+ jobGroupFormat(row, column) {
+ return this.selectDictLabel(this.jobGroupOptions, row.jobGroup);
+ },
+ // 状态字典翻译
+ statusFormat(row, column) {
+ return this.selectDictLabel(this.statusOptions, row.status);
+ },
+ // 取消按钮
+ cancel() {
+ this.open = false;
+ this.reset();
+ },
+ // 表单重置
+ reset() {
+ this.form = {
+ jobId: undefined,
+ jobName: undefined,
+ jobGroup: undefined,
+ invokeTarget: undefined,
+ cronExpression: undefined,
+ misfirePolicy: 1,
+ concurrent: 1,
+ status: "0"
+ };
+ this.resetForm("form");
+ },
+ /** 搜索按钮操作 */
+ handleQuery() {
+ this.queryParams.pageNum = 1;
+ this.getList();
+ },
+ /** 重置按钮操作 */
+ resetQuery() {
+ this.resetForm("queryForm");
+ this.handleQuery();
+ },
+ // 多选框选中数据
+ handleSelectionChange(selection) {
+ this.ids = selection.map(item => item.jobId);
+ this.single = selection.length != 1;
+ this.multiple = !selection.length;
+ },
+ // 任务状态修改
+ handleStatusChange(row) {
+ let text = row.status === "0" ? "启用" : "停用";
+ this.$confirm('确认要"' + text + '""' + row.jobName + '"任务吗?', "警告", {
+ confirmButtonText: "确定",
+ cancelButtonText: "取消",
+ type: "warning"
+ }).then(function() {
+ return changeJobStatus(row.jobId, row.status);
+ }).then(() => {
+ this.msgSuccess(text + "成功");
+ }).catch(function() {
+ row.status = row.status === "0" ? "1" : "0";
+ });
+ },
+ /* 立即执行一次 */
+ handleRun(row) {
+ this.$confirm('确认要立即执行一次"' + row.jobName + '"任务吗?', "警告", {
+ confirmButtonText: "确定",
+ cancelButtonText: "取消",
+ type: "warning"
+ }).then(function() {
+ return runJob(row.jobId, row.jobGroup);
+ }).then(() => {
+ this.msgSuccess("执行成功");
+ }).catch(function() {});
+ },
+ /** 任务详细信息 */
+ handleView(row) {
+ getJob(row.jobId).then(response => {
+ this.form = response.data;
+ this.openView = true;
+ });
+ },
+ /** 任务日志列表查询 */
+ handleJobLog() {
+ this.$router.push("/job/log");
+ },
+ /** 新增按钮操作 */
+ handleAdd() {
+ this.reset();
+ this.open = true;
+ this.title = "添加任务";
+ },
+ /** 修改按钮操作 */
+ handleUpdate(row) {
+ this.reset();
+ const jobId = row.jobId || this.ids;
+ getJob(jobId).then(response => {
+ this.form = response.data;
+ this.open = true;
+ this.title = "修改任务";
+ });
+ },
+ /** 提交按钮 */
+ submitForm: function() {
+ this.$refs["form"].validate(valid => {
+ if (valid) {
+ if (this.form.jobId != undefined) {
+ updateJob(this.form).then(response => {
+ if (response.code === 200) {
+ this.msgSuccess("修改成功");
+ this.open = false;
+ this.getList();
+ } else {
+ this.msgError(response.msg);
+ }
+ });
+ } else {
+ addJob(this.form).then(response => {
+ if (response.code === 200) {
+ this.msgSuccess("新增成功");
+ this.open = false;
+ this.getList();
+ } else {
+ this.msgError(response.msg);
+ }
+ });
+ }
+ }
+ });
+ },
+ /** 删除按钮操作 */
+ handleDelete(row) {
+ const jobIds = row.jobId || this.ids;
+ this.$confirm('是否确认删除定时任务编号为"' + jobIds + '"的数据项?', "警告", {
+ confirmButtonText: "确定",
+ cancelButtonText: "取消",
+ type: "warning"
+ }).then(function() {
+ return delJob(jobIds);
+ }).then(() => {
+ this.getList();
+ this.msgSuccess("删除成功");
+ }).catch(function() {});
+ },
+ /** 导出按钮操作 */
+ handleExport() {
+ this.download('schedule/job/export', {
+ ...this.queryParams
+ }, `job_${new Date().getTime()}.xlsx`)
+ }
+ }
+};
+</script>
\ No newline at end of file
diff --git a/ruoyi-ui/src/views/monitor/job/log.vue b/ruoyi-ui/src/views/monitor/job/log.vue
new file mode 100644
index 0000000..e20813d
--- /dev/null
+++ b/ruoyi-ui/src/views/monitor/job/log.vue
@@ -0,0 +1,288 @@
+<template>
+ <div class="app-container">
+ <el-form :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
+ <el-form-item label="任务名称" prop="jobName">
+ <el-input
+ v-model="queryParams.jobName"
+ placeholder="请输入任务名称"
+ clearable
+ size="small"
+ style="width: 240px"
+ @keyup.enter.native="handleQuery"
+ />
+ </el-form-item>
+ <el-form-item label="任务组名" prop="jobGroup">
+ <el-select
+ v-model="queryParams.jobGroup"
+ placeholder="请任务组名"
+ clearable
+ size="small"
+ style="width: 240px"
+ >
+ <el-option
+ v-for="dict in jobGroupOptions"
+ :key="dict.dictValue"
+ :label="dict.dictLabel"
+ :value="dict.dictValue"
+ />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="执行状态" prop="status">
+ <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 icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+ </el-form-item>
+ </el-form>
+
+ <el-row :gutter="10" class="mb8">
+ <el-col :span="1.5">
+ <el-button
+ type="danger"
+ icon="el-icon-delete"
+ size="mini"
+ :disabled="multiple"
+ @click="handleDelete"
+ v-hasPermi="['monitor:job:remove']"
+ >删除</el-button>
+ </el-col>
+ <el-col :span="1.5">
+ <el-button
+ type="danger"
+ icon="el-icon-delete"
+ size="mini"
+ @click="handleClean"
+ v-hasPermi="['monitor:job:remove']"
+ >清空</el-button>
+ </el-col>
+ <el-col :span="1.5">
+ <el-button
+ type="warning"
+ icon="el-icon-download"
+ size="mini"
+ @click="handleExport"
+ v-hasPermi="['monitor:jobLog:export']"
+ >导出</el-button>
+ </el-col>
+ </el-row>
+
+ <el-table v-loading="loading" :data="jobLogList" @selection-change="handleSelectionChange">
+ <el-table-column type="selection" width="55" align="center" />
+ <el-table-column label="日志编号" width="80" align="center" prop="jobLogId" />
+ <el-table-column label="任务名称" align="center" prop="jobName" :show-overflow-tooltip="true" />
+ <el-table-column label="任务组名" align="center" prop="jobGroup" :formatter="jobGroupFormat" :show-overflow-tooltip="true" />
+ <el-table-column label="调用目标字符串" align="center" prop="invokeTarget" :show-overflow-tooltip="true" />
+ <el-table-column label="日志信息" align="center" prop="jobMessage" :show-overflow-tooltip="true" />
+ <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>{{ parseTime(scope.row.createTime) }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+ <template slot-scope="scope">
+ <el-button
+ size="mini"
+ type="text"
+ icon="el-icon-view"
+ @click="handleView(scope.row)"
+ v-hasPermi="['monitor:job: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" append-to-body>
+ <el-form ref="form" :model="form" label-width="100px" size="mini">
+ <el-row>
+ <el-col :span="12">
+ <el-form-item label="日志序号:">{{ form.jobLogId }}</el-form-item>
+ <el-form-item label="任务名称:">{{ form.jobName }}</el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="任务分组:">{{ form.jobGroup }}</el-form-item>
+ <el-form-item label="执行时间:">{{ form.createTime }}</el-form-item>
+ </el-col>
+ <el-col :span="24">
+ <el-form-item label="调用方法:">{{ form.invokeTarget }}</el-form-item>
+ </el-col>
+ <el-col :span="24">
+ <el-form-item label="日志信息:">{{ form.jobMessage }}</el-form-item>
+ </el-col>
+ <el-col :span="24">
+ <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="24">
+ <el-form-item label="异常信息:" v-if="form.status == 1">{{ form.exceptionInfo }}</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 { listJobLog, delJobLog, cleanJobLog } from "@/api/monitor/jobLog";
+
+export default {
+ name: "JobLog",
+ data() {
+ return {
+ // 遮罩层
+ loading: true,
+ // 选中数组
+ ids: [],
+ // 非多个禁用
+ multiple: true,
+ // 总条数
+ total: 0,
+ // 调度日志表格数据
+ jobLogList: [],
+ // 是否显示弹出层
+ open: false,
+ // 日期范围
+ dateRange: [],
+ // 表单参数
+ form: {},
+ // 执行状态字典
+ statusOptions: [],
+ // 任务组名字典
+ jobGroupOptions: [],
+ // 查询参数
+ queryParams: {
+ pageNum: 1,
+ pageSize: 10,
+ jobName: undefined,
+ jobGroup: undefined,
+ status: undefined
+ },
+ // 表单参数
+ form: {}
+ };
+ },
+ created() {
+ this.getList();
+ this.getDicts("sys_job_status").then(response => {
+ this.statusOptions = response.data;
+ });
+ this.getDicts("sys_job_group").then(response => {
+ this.jobGroupOptions = response.data;
+ });
+ },
+ methods: {
+ /** 查询调度日志列表 */
+ getList() {
+ this.loading = true;
+ listJobLog(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
+ this.jobLogList = response.rows;
+ this.total = response.total;
+ this.loading = false;
+ }
+ );
+ },
+ // 执行状态字典翻译
+ statusFormat(row, column) {
+ return this.selectDictLabel(this.statusOptions, row.status);
+ },
+ // 任务组名字典翻译
+ jobGroupFormat(row, column) {
+ return this.selectDictLabel(this.jobGroupOptions, row.jobGroup);
+ },
+ /** 搜索按钮操作 */
+ handleQuery() {
+ this.queryParams.pageNum = 1;
+ this.getList();
+ },
+ /** 重置按钮操作 */
+ resetQuery() {
+ this.dateRange = [];
+ this.resetForm("queryForm");
+ this.handleQuery();
+ },
+ // 多选框选中数据
+ handleSelectionChange(selection) {
+ this.ids = selection.map(item => item.jobLogId);
+ this.multiple = !selection.length;
+ },
+ /** 详细按钮操作 */
+ handleView(row) {
+ this.open = true;
+ this.form = row;
+ },
+ /** 删除按钮操作 */
+ handleDelete(row) {
+ const jobLogIds = this.ids;
+ this.$confirm('是否确认删除调度日志编号为"' + jobLogIds + '"的数据项?', "警告", {
+ confirmButtonText: "确定",
+ cancelButtonText: "取消",
+ type: "warning"
+ }).then(function() {
+ return delJobLog(jobLogIds);
+ }).then(() => {
+ this.getList();
+ this.msgSuccess("删除成功");
+ }).catch(function() {});
+ },
+ /** 清空按钮操作 */
+ handleClean() {
+ this.$confirm("是否确认清空所有调度日志数据项?", "警告", {
+ confirmButtonText: "确定",
+ cancelButtonText: "取消",
+ type: "warning"
+ }).then(function() {
+ return cleanJobLog();
+ }).then(() => {
+ this.getList();
+ this.msgSuccess("清空成功");
+ }).catch(function() {});
+ },
+ /** 导出按钮操作 */
+ handleExport() {
+ this.download('schedule/job/log/export', {
+ ...this.queryParams
+ }, `log_${new Date().getTime()}.xlsx`)
+ }
+ }
+};
+</script>
\ No newline at end of file
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..72c4a53
--- /dev/null
+++ b/ruoyi-ui/src/views/monitor/online/index.vue
@@ -0,0 +1,128 @@
+<template>
+ <div class="app-container">
+ <el-form :model="queryParams" ref="queryForm" :inline="true">
+ <el-form-item label="登录地址" prop="ipaddr">
+ <el-input
+ v-model="queryParams.ipaddr"
+ placeholder="请输入登录地址"
+ clearable
+ size="small"
+ @keyup.enter.native="handleQuery"
+ />
+ </el-form-item>
+ <el-form-item label="用户名称" prop="userName">
+ <el-input
+ v-model="queryParams.userName"
+ placeholder="请输入用户名称"
+ clearable
+ size="small"
+ @keyup.enter.native="handleQuery"
+ />
+ </el-form-item>
+ <el-form-item>
+ <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+ <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+ </el-form-item>
+ </el-form>
+
+ <el-table
+ v-loading="loading"
+ :data="list.slice((pageNum-1)*pageSize,pageNum*pageSize)"
+ style="width: 100%;"
+ >
+ <el-table-column label="序号" type="index" align="center">
+ <template slot-scope="scope">
+ <span>{{(pageNum - 1) * pageSize + scope.$index + 1}}</span>
+ </template>
+ </el-table-column>
+ <el-table-column label="会话编号" align="center" prop="tokenId" :show-overflow-tooltip="true" />
+ <el-table-column label="登录名称" align="center" prop="userName" :show-overflow-tooltip="true" />
+ <el-table-column label="部门名称" align="center" prop="deptName" />
+ <el-table-column label="主机" align="center" prop="ipaddr" :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="loginTime" width="180">
+ <template slot-scope="scope">
+ <span>{{ parseTime(scope.row.loginTime) }}</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-delete"
+ @click="handleForceLogout(scope.row)"
+ v-hasPermi="['monitor:online:forceLogout']"
+ >强退</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+
+ <pagination v-show="total>0" :total="total" :page.sync="pageNum" :limit.sync="pageSize" />
+ </div>
+</template>
+
+<script>
+import { list, forceLogout } from "@/api/monitor/online";
+
+export default {
+ name: "Online",
+ data() {
+ return {
+ // 遮罩层
+ loading: true,
+ // 总条数
+ total: 0,
+ // 表格数据
+ list: [],
+ pageNum: 1,
+ pageSize: 10,
+ // 查询参数
+ queryParams: {
+ ipaddr: undefined,
+ userName: undefined
+ }
+ };
+ },
+ created() {
+ this.getList();
+ },
+ methods: {
+ /** 查询登录日志列表 */
+ getList() {
+ this.loading = true;
+ list(this.queryParams).then(response => {
+ this.list = response.rows;
+ this.total = response.total;
+ this.loading = false;
+ });
+ },
+ /** 搜索按钮操作 */
+ handleQuery() {
+ this.pageNum = 1;
+ this.getList();
+ },
+ /** 重置按钮操作 */
+ resetQuery() {
+ this.resetForm("queryForm");
+ this.handleQuery();
+ },
+ /** 强退按钮操作 */
+ handleForceLogout(row) {
+ this.$confirm('是否确认强退名称为"' + row.userName + '"的数据项?', "警告", {
+ confirmButtonText: "确定",
+ cancelButtonText: "取消",
+ type: "warning"
+ }).then(function() {
+ return forceLogout(row.tokenId);
+ }).then(() => {
+ this.getList();
+ this.msgSuccess("强退成功");
+ }).catch(function() {});
+ }
+ }
+};
+</script>
+
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..1873012
--- /dev/null
+++ b/ruoyi-ui/src/views/system/config/index.vue
@@ -0,0 +1,339 @@
+<template>
+ <div class="app-container">
+ <el-form :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
+ <el-form-item label="参数名称" prop="configName">
+ <el-input
+ v-model="queryParams.configName"
+ placeholder="请输入参数名称"
+ clearable
+ size="small"
+ style="width: 240px"
+ @keyup.enter.native="handleQuery"
+ />
+ </el-form-item>
+ <el-form-item label="参数键名" prop="configKey">
+ <el-input
+ v-model="queryParams.configKey"
+ placeholder="请输入参数键名"
+ clearable
+ size="small"
+ style="width: 240px"
+ @keyup.enter.native="handleQuery"
+ />
+ </el-form-item>
+ <el-form-item label="系统内置" prop="configType">
+ <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 icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+ </el-form-item>
+ </el-form>
+
+ <el-row :gutter="10" class="mb8">
+ <el-col :span="1.5">
+ <el-button
+ type="primary"
+ icon="el-icon-plus"
+ size="mini"
+ @click="handleAdd"
+ v-hasPermi="['system:config:add']"
+ >新增</el-button>
+ </el-col>
+ <el-col :span="1.5">
+ <el-button
+ type="success"
+ icon="el-icon-edit"
+ size="mini"
+ :disabled="single"
+ @click="handleUpdate"
+ v-hasPermi="['system:config:edit']"
+ >修改</el-button>
+ </el-col>
+ <el-col :span="1.5">
+ <el-button
+ type="danger"
+ icon="el-icon-delete"
+ size="mini"
+ :disabled="multiple"
+ @click="handleDelete"
+ v-hasPermi="['system:config:remove']"
+ >删除</el-button>
+ </el-col>
+ <el-col :span="1.5">
+ <el-button
+ type="warning"
+ icon="el-icon-download"
+ size="mini"
+ @click="handleExport"
+ v-hasPermi="['system:config:export']"
+ >导出</el-button>
+ </el-col>
+ </el-row>
+
+ <el-table v-loading="loading" :data="configList" @selection-change="handleSelectionChange">
+ <el-table-column type="selection" width="55" align="center" />
+ <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>{{ parseTime(scope.row.createTime) }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+ <template slot-scope="scope">
+ <el-button
+ size="mini"
+ type="text"
+ icon="el-icon-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" append-to-body>
+ <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 {
+ name: "Config",
+ data() {
+ return {
+ // 遮罩层
+ loading: true,
+ // 选中数组
+ ids: [],
+ // 非单个禁用
+ single: true,
+ // 非多个禁用
+ multiple: 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();
+ },
+ /** 重置按钮操作 */
+ resetQuery() {
+ this.dateRange = [];
+ this.resetForm("queryForm");
+ this.handleQuery();
+ },
+ /** 新增按钮操作 */
+ handleAdd() {
+ this.reset();
+ this.open = true;
+ this.title = "添加参数";
+ },
+ // 多选框选中数据
+ handleSelectionChange(selection) {
+ this.ids = selection.map(item => item.configId)
+ this.single = selection.length!=1
+ this.multiple = !selection.length
+ },
+ /** 修改按钮操作 */
+ handleUpdate(row) {
+ this.reset();
+ const configId = row.configId || this.ids
+ getConfig(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) {
+ const configIds = row.configId || this.ids;
+ this.$confirm('是否确认删除参数编号为"' + configIds + '"的数据项?', "警告", {
+ confirmButtonText: "确定",
+ cancelButtonText: "取消",
+ type: "warning"
+ }).then(function() {
+ return delConfig(configIds);
+ }).then(() => {
+ this.getList();
+ this.msgSuccess("删除成功");
+ }).catch(function() {});
+ },
+ /** 导出按钮操作 */
+ handleExport() {
+ this.download('system/config/export', {
+ ...this.queryParams
+ }, `config_${new Date().getTime()}.xlsx`)
+ }
+ }
+};
+</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..ac6d663
--- /dev/null
+++ b/ruoyi-ui/src/views/system/dept/index.vue
@@ -0,0 +1,317 @@
+<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="260"></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>{{ parseTime(scope.row.createTime) }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+ <template slot-scope="scope">
+ <el-button
+ size="mini"
+ type="text"
+ icon="el-icon-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" append-to-body>
+ <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" :normalizer="normalizer" 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, delDept, addDept, updateDept, listDeptExcludeChild } from "@/api/system/dept";
+import Treeselect from "@riophae/vue-treeselect";
+import "@riophae/vue-treeselect/dist/vue-treeselect.css";
+
+export default {
+ name: "Dept",
+ components: { Treeselect },
+ data() {
+ return {
+ // 遮罩层
+ loading: true,
+ // 表格树数据
+ deptList: [],
+ // 部门树选项
+ deptOptions: [],
+ // 弹出层标题
+ 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 = this.handleTree(response.data, "deptId");
+ this.loading = false;
+ });
+ },
+ /** 转换部门数据结构 */
+ normalizer(node) {
+ if (node.children && !node.children.length) {
+ delete node.children;
+ }
+ return {
+ id: node.deptId,
+ label: node.deptName,
+ children: node.children
+ };
+ },
+ // 字典状态字典翻译
+ statusFormat(row, column) {
+ return this.selectDictLabel(this.statusOptions, row.status);
+ },
+ // 取消按钮
+ cancel() {
+ this.open = false;
+ this.reset();
+ },
+ // 表单重置
+ reset() {
+ this.form = {
+ deptId: undefined,
+ parentId: undefined,
+ deptName: undefined,
+ orderNum: undefined,
+ leader: undefined,
+ phone: undefined,
+ email: undefined,
+ status: "0"
+ };
+ this.resetForm("form");
+ },
+ /** 搜索按钮操作 */
+ handleQuery() {
+ this.getList();
+ },
+ /** 新增按钮操作 */
+ handleAdd(row) {
+ this.reset();
+ if (row != undefined) {
+ this.form.parentId = row.deptId;
+ }
+ this.open = true;
+ this.title = "添加部门";
+ listDept().then(response => {
+ this.deptOptions = this.handleTree(response.data, "deptId");
+ });
+ },
+ /** 修改按钮操作 */
+ handleUpdate(row) {
+ this.reset();
+ getDept(row.deptId).then(response => {
+ this.form = response.data;
+ this.open = true;
+ this.title = "修改部门";
+ });
+ listDeptExcludeChild(row.deptId).then(response => {
+ this.deptOptions = this.handleTree(response.data, "deptId");
+ });
+ },
+ /** 提交按钮 */
+ 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..13207c2
--- /dev/null
+++ b/ruoyi-ui/src/views/system/dict/data.vue
@@ -0,0 +1,348 @@
+<template>
+ <div class="app-container">
+ <el-form :model="queryParams" ref="queryForm" :inline="true">
+ <el-form-item label="字典名称" prop="dictType">
+ <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="字典标签" prop="dictLabel">
+ <el-input
+ v-model="queryParams.dictLabel"
+ placeholder="请输入字典标签"
+ clearable
+ size="small"
+ @keyup.enter.native="handleQuery"
+ />
+ </el-form-item>
+ <el-form-item label="状态" prop="status">
+ <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 icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+ </el-form-item>
+ </el-form>
+
+ <el-row :gutter="10" class="mb8">
+ <el-col :span="1.5">
+ <el-button
+ type="primary"
+ icon="el-icon-plus"
+ size="mini"
+ @click="handleAdd"
+ v-hasPermi="['system:dict:add']"
+ >新增</el-button>
+ </el-col>
+ <el-col :span="1.5">
+ <el-button
+ type="success"
+ icon="el-icon-edit"
+ size="mini"
+ :disabled="single"
+ @click="handleUpdate"
+ v-hasPermi="['system:dict:edit']"
+ >修改</el-button>
+ </el-col>
+ <el-col :span="1.5">
+ <el-button
+ type="danger"
+ icon="el-icon-delete"
+ size="mini"
+ :disabled="multiple"
+ @click="handleDelete"
+ v-hasPermi="['system:dict:remove']"
+ >删除</el-button>
+ </el-col>
+ <el-col :span="1.5">
+ <el-button
+ type="warning"
+ icon="el-icon-download"
+ size="mini"
+ @click="handleExport"
+ v-hasPermi="['system:dict:export']"
+ >导出</el-button>
+ </el-col>
+ </el-row>
+
+ <el-table v-loading="loading" :data="dataList" @selection-change="handleSelectionChange">
+ <el-table-column type="selection" width="55" align="center" />
+ <el-table-column label="字典编码" align="center" prop="dictCode" />
+ <el-table-column label="字典标签" align="center" prop="dictLabel" />
+ <el-table-column label="字典键值" align="center" prop="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>{{ parseTime(scope.row.createTime) }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+ <template slot-scope="scope">
+ <el-button
+ size="mini"
+ type="text"
+ icon="el-icon-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" append-to-body>
+ <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 {
+ name: "Data",
+ data() {
+ return {
+ // 遮罩层
+ loading: true,
+ // 选中数组
+ ids: [],
+ // 非单个禁用
+ single: true,
+ // 非多个禁用
+ multiple: true,
+ // 总条数
+ total: 0,
+ // 字典表格数据
+ dataList: [],
+ // 默认字典类型
+ defaultDictType: "",
+ // 弹出层标题
+ 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.defaultDictType = 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();
+ },
+ /** 重置按钮操作 */
+ resetQuery() {
+ this.resetForm("queryForm");
+ this.queryParams.dictType = this.defaultDictType;
+ this.handleQuery();
+ },
+ /** 新增按钮操作 */
+ handleAdd() {
+ this.reset();
+ this.open = true;
+ this.title = "添加字典数据";
+ this.form.dictType = this.queryParams.dictType;
+ },
+ // 多选框选中数据
+ handleSelectionChange(selection) {
+ this.ids = selection.map(item => item.dictCode)
+ this.single = selection.length!=1
+ this.multiple = !selection.length
+ },
+ /** 修改按钮操作 */
+ handleUpdate(row) {
+ this.reset();
+ const dictCode = row.dictCode || this.ids
+ getData(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) {
+ const dictCodes = row.dictCode || this.ids;
+ this.$confirm('是否确认删除字典编码为"' + dictCodes + '"的数据项?', "警告", {
+ confirmButtonText: "确定",
+ cancelButtonText: "取消",
+ type: "warning"
+ }).then(function() {
+ return delData(dictCodes);
+ }).then(() => {
+ this.getList();
+ this.msgSuccess("删除成功");
+ }).catch(function() {});
+ },
+ /** 导出按钮操作 */
+ handleExport() {
+ this.download('system/dict/data/export', {
+ ...this.queryParams
+ }, `data_${new Date().getTime()}.xlsx`)
+ }
+ }
+};
+</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..706804c
--- /dev/null
+++ b/ruoyi-ui/src/views/system/dict/index.vue
@@ -0,0 +1,343 @@
+<template>
+ <div class="app-container">
+ <el-form :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
+ <el-form-item label="字典名称" prop="dictName">
+ <el-input
+ v-model="queryParams.dictName"
+ placeholder="请输入字典名称"
+ clearable
+ size="small"
+ style="width: 240px"
+ @keyup.enter.native="handleQuery"
+ />
+ </el-form-item>
+ <el-form-item label="字典类型" prop="dictType">
+ <el-input
+ v-model="queryParams.dictType"
+ placeholder="请输入字典类型"
+ clearable
+ size="small"
+ style="width: 240px"
+ @keyup.enter.native="handleQuery"
+ />
+ </el-form-item>
+ <el-form-item label="状态" prop="status">
+ <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 icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+ </el-form-item>
+ </el-form>
+
+ <el-row :gutter="10" class="mb8">
+ <el-col :span="1.5">
+ <el-button
+ type="primary"
+ icon="el-icon-plus"
+ size="mini"
+ @click="handleAdd"
+ v-hasPermi="['system:dict:add']"
+ >新增</el-button>
+ </el-col>
+ <el-col :span="1.5">
+ <el-button
+ type="success"
+ icon="el-icon-edit"
+ size="mini"
+ :disabled="single"
+ @click="handleUpdate"
+ v-hasPermi="['system:dict:edit']"
+ >修改</el-button>
+ </el-col>
+ <el-col :span="1.5">
+ <el-button
+ type="danger"
+ icon="el-icon-delete"
+ size="mini"
+ :disabled="multiple"
+ @click="handleDelete"
+ v-hasPermi="['system:dict:remove']"
+ >删除</el-button>
+ </el-col>
+ <el-col :span="1.5">
+ <el-button
+ type="warning"
+ icon="el-icon-download"
+ size="mini"
+ @click="handleExport"
+ v-hasPermi="['system:dict:export']"
+ >导出</el-button>
+ </el-col>
+ </el-row>
+
+ <el-table v-loading="loading" :data="typeList" @selection-change="handleSelectionChange">
+ <el-table-column type="selection" width="55" align="center" />
+ <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" :show-overflow-tooltip="true">
+ <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>{{ parseTime(scope.row.createTime) }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+ <template slot-scope="scope">
+ <el-button
+ size="mini"
+ type="text"
+ icon="el-icon-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" append-to-body>
+ <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 {
+ name: "Dict",
+ data() {
+ return {
+ // 遮罩层
+ loading: true,
+ // 选中数组
+ ids: [],
+ // 非单个禁用
+ single: true,
+ // 非多个禁用
+ multiple: 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();
+ },
+ /** 重置按钮操作 */
+ resetQuery() {
+ this.dateRange = [];
+ this.resetForm("queryForm");
+ this.handleQuery();
+ },
+ /** 新增按钮操作 */
+ handleAdd() {
+ this.reset();
+ this.open = true;
+ this.title = "添加字典类型";
+ },
+ // 多选框选中数据
+ handleSelectionChange(selection) {
+ this.ids = selection.map(item => item.dictId)
+ this.single = selection.length!=1
+ this.multiple = !selection.length
+ },
+ /** 修改按钮操作 */
+ handleUpdate(row) {
+ this.reset();
+ const dictId = row.dictId || this.ids
+ getType(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) {
+ const dictIds = row.dictId || this.ids;
+ this.$confirm('是否确认删除字典编号为"' + dictIds + '"的数据项?', "警告", {
+ confirmButtonText: "确定",
+ cancelButtonText: "取消",
+ type: "warning"
+ }).then(function() {
+ return delType(dictIds);
+ }).then(() => {
+ this.getList();
+ this.msgSuccess("删除成功");
+ }).catch(function() {});
+ },
+ /** 导出按钮操作 */
+ handleExport() {
+ this.download('system/dict/type/export', {
+ ...this.queryParams
+ }, `type_${new Date().getTime()}.xlsx`)
+ }
+ }
+};
+</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/logininfor/index.vue b/ruoyi-ui/src/views/system/logininfor/index.vue
new file mode 100644
index 0000000..cf71a0a
--- /dev/null
+++ b/ruoyi-ui/src/views/system/logininfor/index.vue
@@ -0,0 +1,220 @@
+<template>
+ <div class="app-container">
+ <el-form :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
+ <el-form-item label="登录地址" prop="ipaddr">
+ <el-input
+ v-model="queryParams.ipaddr"
+ placeholder="请输入登录地址"
+ clearable
+ style="width: 240px;"
+ size="small"
+ @keyup.enter.native="handleQuery"
+ />
+ </el-form-item>
+ <el-form-item label="用户名称" prop="userName">
+ <el-input
+ v-model="queryParams.userName"
+ placeholder="请输入用户名称"
+ clearable
+ style="width: 240px;"
+ size="small"
+ @keyup.enter.native="handleQuery"
+ />
+ </el-form-item>
+ <el-form-item label="状态" prop="status">
+ <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 icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+ </el-form-item>
+ </el-form>
+
+ <el-row :gutter="10" class="mb8">
+ <el-col :span="1.5">
+ <el-button
+ type="danger"
+ icon="el-icon-delete"
+ size="mini"
+ :disabled="multiple"
+ @click="handleDelete"
+ v-hasPermi="['system:logininfor:remove']"
+ >删除</el-button>
+ </el-col>
+ <el-col :span="1.5">
+ <el-button
+ type="danger"
+ icon="el-icon-delete"
+ size="mini"
+ @click="handleClean"
+ v-hasPermi="['system:logininfor:remove']"
+ >清空</el-button>
+ </el-col>
+ <el-col :span="1.5">
+ <el-button
+ type="warning"
+ icon="el-icon-download"
+ size="mini"
+ @click="handleExport"
+ v-hasPermi="['system:logininfor:export']"
+ >导出</el-button>
+ </el-col>
+ </el-row>
+
+ <el-table v-loading="loading" :data="list" @selection-change="handleSelectionChange">
+ <el-table-column type="selection" width="55" align="center" />
+ <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" :show-overflow-tooltip="true" />
+ <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>{{ parseTime(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, delLogininfor, cleanLogininfor } from "@/api/system/logininfor";
+
+export default {
+ name: "Logininfor",
+ data() {
+ return {
+ // 遮罩层
+ loading: true,
+ // 选中数组
+ ids: [],
+ // 非多个禁用
+ multiple: 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();
+ },
+ /** 重置按钮操作 */
+ resetQuery() {
+ this.dateRange = [];
+ this.resetForm("queryForm");
+ this.handleQuery();
+ },
+ // 多选框选中数据
+ handleSelectionChange(selection) {
+ this.ids = selection.map(item => item.infoId)
+ this.multiple = !selection.length
+ },
+ /** 删除按钮操作 */
+ handleDelete(row) {
+ const infoIds = row.infoId || this.ids;
+ this.$confirm('是否确认删除访问编号为"' + infoIds + '"的数据项?', "警告", {
+ confirmButtonText: "确定",
+ cancelButtonText: "取消",
+ type: "warning"
+ }).then(function() {
+ return delLogininfor(infoIds);
+ }).then(() => {
+ this.getList();
+ this.msgSuccess("删除成功");
+ }).catch(function() {});
+ },
+ /** 清空按钮操作 */
+ handleClean() {
+ this.$confirm('是否确认清空所有登录日志数据项?', "警告", {
+ confirmButtonText: "确定",
+ cancelButtonText: "取消",
+ type: "warning"
+ }).then(function() {
+ return cleanLogininfor();
+ }).then(() => {
+ this.getList();
+ this.msgSuccess("清空成功");
+ }).catch(function() {});
+ },
+ /** 导出按钮操作 */
+ handleExport() {
+ this.download('system/logininfor/export', {
+ ...this.queryParams
+ }, `logininfor_${new Date().getTime()}.xlsx`)
+ }
+ }
+};
+</script>
+
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..74b4378
--- /dev/null
+++ b/ruoyi-ui/src/views/system/menu/index.vue
@@ -0,0 +1,376 @@
+<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.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: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="160"></el-table-column>
+ <el-table-column prop="icon" label="图标" align="center" width="100">
+ <template slot-scope="scope">
+ <svg-icon :icon-class="scope.row.icon" />
+ </template>
+ </el-table-column>
+ <el-table-column prop="orderNum" label="排序" width="60"></el-table-column>
+ <el-table-column prop="perms" label="权限标识" :show-overflow-tooltip="true"></el-table-column>
+ <el-table-column prop="component" label="组件路径" :show-overflow-tooltip="true"></el-table-column>
+ <el-table-column prop="status" label="状态" :formatter="statusFormat" width="80"></el-table-column>
+ <el-table-column label="创建时间" align="center" prop="createTime">
+ <template slot-scope="scope">
+ <span>{{ parseTime(scope.row.createTime) }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+ <template slot-scope="scope">
+ <el-button size="mini"
+ type="text"
+ icon="el-icon-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
+ 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" append-to-body>
+ <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+ <el-row>
+ <el-col :span="24">
+ <el-form-item label="上级菜单">
+ <treeselect
+ v-model="form.parentId"
+ :options="menuOptions"
+ :normalizer="normalizer"
+ :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 != 'M'" label="权限标识">
+ <el-input v-model="form.perms" placeholder="请权限标识" maxlength="50" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <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-col :span="12">
+ <el-form-item v-if="form.menuType != 'F'" 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 { listMenu, getMenu, 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 {
+ name: "Menu",
+ components: { Treeselect, IconSelect },
+ data() {
+ return {
+ // 遮罩层
+ loading: true,
+ // 菜单表格树数据
+ menuList: [],
+ // 菜单树选项
+ menuOptions: [],
+ // 弹出层标题
+ title: "",
+ // 是否显示弹出层
+ open: false,
+ // 显示状态数据字典
+ visibleOptions: [],
+ // 菜单状态数据字典
+ statusOptions: [],
+ // 查询参数
+ queryParams: {
+ menuName: undefined,
+ visible: undefined
+ },
+ // 表单参数
+ form: {},
+ // 表单校验
+ rules: {
+ menuName: [
+ { required: true, message: "菜单名称不能为空", trigger: "blur" }
+ ],
+ orderNum: [
+ { required: true, message: "菜单顺序不能为空", trigger: "blur" }
+ ],
+ path: [
+ { required: true, message: "路由地址不能为空", trigger: "blur" }
+ ]
+ }
+ };
+ },
+ created() {
+ this.getList();
+ this.getDicts("sys_show_hide").then(response => {
+ this.visibleOptions = response.data;
+ });
+ this.getDicts("sys_normal_disable").then(response => {
+ this.statusOptions = response.data;
+ });
+ },
+ methods: {
+ // 选择图标
+ selected(name) {
+ this.form.icon = name;
+ },
+ /** 查询菜单列表 */
+ getList() {
+ this.loading = true;
+ listMenu(this.queryParams).then(response => {
+ this.menuList = this.handleTree(response.data, "menuId");
+ this.loading = false;
+ });
+ },
+ /** 转换菜单数据结构 */
+ normalizer(node) {
+ if (node.children && !node.children.length) {
+ delete node.children;
+ }
+ return {
+ id: node.menuId,
+ label: node.menuName,
+ children: node.children
+ };
+ },
+ /** 查询菜单下拉树结构 */
+ getTreeselect() {
+ listMenu().then(response => {
+ this.menuOptions = [];
+ const menu = { menuId: 0, menuName: '主类目', children: [] };
+ menu.children = this.handleTree(response.data, "menuId");
+ this.menuOptions.push(menu);
+ });
+ },
+ // 显示状态字典翻译
+ visibleFormat(row, column) {
+ if (row.menuType == "F") {
+ return "";
+ }
+ return this.selectDictLabel(this.visibleOptions, row.visible);
+ },
+ // 菜单状态字典翻译
+ statusFormat(row, column) {
+ if (row.menuType == "F") {
+ return "";
+ }
+ return this.selectDictLabel(this.statusOptions, row.status);
+ },
+ // 取消按钮
+ 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",
+ status: "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..50dcd3e
--- /dev/null
+++ b/ruoyi-ui/src/views/system/notice/index.vue
@@ -0,0 +1,345 @@
+<template>
+ <div class="app-container">
+ <el-form :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
+ <el-form-item label="公告标题" prop="noticeTitle">
+ <el-input
+ v-model="queryParams.noticeTitle"
+ placeholder="请输入公告标题"
+ clearable
+ size="small"
+ @keyup.enter.native="handleQuery"
+ />
+ </el-form-item>
+ <el-form-item label="操作人员" prop="createBy">
+ <el-input
+ v-model="queryParams.createBy"
+ placeholder="请输入操作人员"
+ clearable
+ size="small"
+ @keyup.enter.native="handleQuery"
+ />
+ </el-form-item>
+ <el-form-item label="类型" prop="noticeType">
+ <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 icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+ </el-form-item>
+ </el-form>
+
+ <el-row :gutter="10" class="mb8">
+ <el-col :span="1.5">
+ <el-button
+ type="primary"
+ icon="el-icon-plus"
+ size="mini"
+ @click="handleAdd"
+ v-hasPermi="['system:notice:add']"
+ >新增</el-button>
+ </el-col>
+ <el-col :span="1.5">
+ <el-button
+ type="success"
+ icon="el-icon-edit"
+ size="mini"
+ :disabled="single"
+ @click="handleUpdate"
+ v-hasPermi="['system:notice:edit']"
+ >修改</el-button>
+ </el-col>
+ <el-col :span="1.5">
+ <el-button
+ type="danger"
+ icon="el-icon-delete"
+ size="mini"
+ :disabled="multiple"
+ @click="handleDelete"
+ v-hasPermi="['system:notice:remove']"
+ >删除</el-button>
+ </el-col>
+ </el-row>
+
+ <el-table v-loading="loading" :data="noticeList" @selection-change="handleSelectionChange">
+ <el-table-column type="selection" width="55" align="center" />
+ <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>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</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" append-to-body>
+ <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, exportNotice } from "@/api/system/notice";
+import Editor from '@/components/Editor';
+
+export default {
+ name: "Notice",
+ components: {
+ Editor
+ },
+ data() {
+ return {
+ // 遮罩层
+ loading: true,
+ // 选中数组
+ ids: [],
+ // 非单个禁用
+ single: true,
+ // 非多个禁用
+ multiple: 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();
+ },
+ /** 重置按钮操作 */
+ resetQuery() {
+ this.resetForm("queryForm");
+ this.handleQuery();
+ },
+ // 多选框选中数据
+ handleSelectionChange(selection) {
+ this.ids = selection.map(item => item.noticeId)
+ this.single = selection.length!=1
+ this.multiple = !selection.length
+ },
+ /** 新增按钮操作 */
+ handleAdd() {
+ this.reset();
+ this.open = true;
+ this.title = "添加公告";
+ },
+ /** 修改按钮操作 */
+ handleUpdate(row) {
+ this.reset();
+ const noticeId = row.noticeId || this.ids
+ getNotice(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) {
+ const noticeIds = row.noticeId || this.ids
+ this.$confirm('是否确认删除公告编号为"' + noticeIds + '"的数据项?', "警告", {
+ confirmButtonText: "确定",
+ cancelButtonText: "取消",
+ type: "warning"
+ }).then(function() {
+ return delNotice(noticeIds);
+ }).then(() => {
+ this.getList();
+ this.msgSuccess("删除成功");
+ }).catch(function() {});
+ }
+ }
+};
+</script>
\ No newline at end of file
diff --git a/ruoyi-ui/src/views/system/operlog/index.vue b/ruoyi-ui/src/views/system/operlog/index.vue
new file mode 100644
index 0000000..d417c91
--- /dev/null
+++ b/ruoyi-ui/src/views/system/operlog/index.vue
@@ -0,0 +1,308 @@
+<template>
+ <div class="app-container">
+ <el-form :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
+ <el-form-item label="系统模块" prop="title">
+ <el-input
+ v-model="queryParams.title"
+ placeholder="请输入系统模块"
+ clearable
+ style="width: 240px;"
+ size="small"
+ @keyup.enter.native="handleQuery"
+ />
+ </el-form-item>
+ <el-form-item label="操作人员" prop="operName">
+ <el-input
+ v-model="queryParams.operName"
+ placeholder="请输入操作人员"
+ clearable
+ style="width: 240px;"
+ size="small"
+ @keyup.enter.native="handleQuery"
+ />
+ </el-form-item>
+ <el-form-item label="类型" prop="businessType">
+ <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="状态" prop="status">
+ <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 icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+ </el-form-item>
+ </el-form>
+
+ <el-row :gutter="10" class="mb8">
+ <el-col :span="1.5">
+ <el-button
+ type="danger"
+ icon="el-icon-delete"
+ size="mini"
+ :disabled="multiple"
+ @click="handleDelete"
+ v-hasPermi="['system:operlog:remove']"
+ >删除</el-button>
+ </el-col>
+ <el-col :span="1.5">
+ <el-button
+ type="danger"
+ icon="el-icon-delete"
+ size="mini"
+ @click="handleClean"
+ v-hasPermi="['system:operlog:remove']"
+ >清空</el-button>
+ </el-col>
+ <el-col :span="1.5">
+ <el-button
+ type="warning"
+ icon="el-icon-download"
+ size="mini"
+ @click="handleExport"
+ v-hasPermi="['system:config:export']"
+ >导出</el-button>
+ </el-col>
+ </el-row>
+
+ <el-table v-loading="loading" :data="list" @selection-change="handleSelectionChange">
+ <el-table-column type="selection" width="55" align="center" />
+ <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" :show-overflow-tooltip="true" />
+ <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>{{ parseTime(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="['system: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" append-to-body>
+ <el-form ref="form" :model="form" label-width="100px" size="mini">
+ <el-row>
+ <el-col :span="12">
+ <el-form-item label="操作模块:">{{ form.title }} / {{ typeFormat(form) }}</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="操作时间:">{{ parseTime(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, delOperlog, cleanOperlog } from "@/api/system/operlog";
+
+export default {
+ name: "Operlog",
+ data() {
+ return {
+ // 遮罩层
+ loading: true,
+ // 选中数组
+ ids: [],
+ // 非多个禁用
+ multiple: 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();
+ },
+ /** 重置按钮操作 */
+ resetQuery() {
+ this.dateRange = [];
+ this.resetForm("queryForm");
+ this.handleQuery();
+ },
+ // 多选框选中数据
+ handleSelectionChange(selection) {
+ this.ids = selection.map(item => item.operId)
+ this.multiple = !selection.length
+ },
+ /** 详细按钮操作 */
+ handleView(row) {
+ this.open = true;
+ this.form = row;
+ },
+ /** 删除按钮操作 */
+ handleDelete(row) {
+ const operIds = row.operId || this.ids;
+ this.$confirm('是否确认删除日志编号为"' + operIds + '"的数据项?', "警告", {
+ confirmButtonText: "确定",
+ cancelButtonText: "取消",
+ type: "warning"
+ }).then(function() {
+ return delOperlog(operIds);
+ }).then(() => {
+ this.getList();
+ this.msgSuccess("删除成功");
+ }).catch(function() {});
+ },
+ /** 清空按钮操作 */
+ handleClean() {
+ this.$confirm('是否确认清空所有操作日志数据项?', "警告", {
+ confirmButtonText: "确定",
+ cancelButtonText: "取消",
+ type: "warning"
+ }).then(function() {
+ return cleanOperlog();
+ }).then(() => {
+ this.getList();
+ this.msgSuccess("清空成功");
+ }).catch(function() {});
+ },
+ /** 导出按钮操作 */
+ handleExport() {
+ this.download('system/operlog/export', {
+ ...this.queryParams
+ }, `operlog_${new Date().getTime()}.xlsx`)
+ }
+ }
+};
+</script>
+
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..61261d6
--- /dev/null
+++ b/ruoyi-ui/src/views/system/post/index.vue
@@ -0,0 +1,320 @@
+<template>
+ <div class="app-container">
+ <el-form :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
+ <el-form-item label="岗位编码" prop="postCode">
+ <el-input
+ v-model="queryParams.postCode"
+ placeholder="请输入岗位编码"
+ clearable
+ size="small"
+ @keyup.enter.native="handleQuery"
+ />
+ </el-form-item>
+ <el-form-item label="岗位名称" prop="postName">
+ <el-input
+ v-model="queryParams.postName"
+ placeholder="请输入岗位名称"
+ clearable
+ size="small"
+ @keyup.enter.native="handleQuery"
+ />
+ </el-form-item>
+ <el-form-item label="状态" prop="status">
+ <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 icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+ </el-form-item>
+ </el-form>
+
+ <el-row :gutter="10" class="mb8">
+ <el-col :span="1.5">
+ <el-button
+ type="primary"
+ icon="el-icon-plus"
+ size="mini"
+ @click="handleAdd"
+ v-hasPermi="['system:post:add']"
+ >新增</el-button>
+ </el-col>
+ <el-col :span="1.5">
+ <el-button
+ type="success"
+ icon="el-icon-edit"
+ size="mini"
+ :disabled="single"
+ @click="handleUpdate"
+ v-hasPermi="['system:post:edit']"
+ >修改</el-button>
+ </el-col>
+ <el-col :span="1.5">
+ <el-button
+ type="danger"
+ icon="el-icon-delete"
+ size="mini"
+ :disabled="multiple"
+ @click="handleDelete"
+ v-hasPermi="['system:post:remove']"
+ >删除</el-button>
+ </el-col>
+ <el-col :span="1.5">
+ <el-button
+ type="warning"
+ icon="el-icon-download"
+ size="mini"
+ @click="handleExport"
+ v-hasPermi="['system:post:export']"
+ >导出</el-button>
+ </el-col>
+ </el-row>
+
+ <el-table v-loading="loading" :data="postList" @selection-change="handleSelectionChange">
+ <el-table-column type="selection" width="55" align="center" />
+ <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>{{ parseTime(scope.row.createTime) }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+ <template slot-scope="scope">
+ <el-button
+ size="mini"
+ type="text"
+ icon="el-icon-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" append-to-body>
+ <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 {
+ name: "Post",
+ data() {
+ return {
+ // 遮罩层
+ loading: true,
+ // 选中数组
+ ids: [],
+ // 非单个禁用
+ single: true,
+ // 非多个禁用
+ multiple: 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();
+ },
+ /** 重置按钮操作 */
+ resetQuery() {
+ this.resetForm("queryForm");
+ this.handleQuery();
+ },
+ // 多选框选中数据
+ handleSelectionChange(selection) {
+ this.ids = selection.map(item => item.postId)
+ this.single = selection.length!=1
+ this.multiple = !selection.length
+ },
+ /** 新增按钮操作 */
+ handleAdd() {
+ this.reset();
+ this.open = true;
+ this.title = "添加岗位";
+ },
+ /** 修改按钮操作 */
+ handleUpdate(row) {
+ this.reset();
+ const postId = row.postId || this.ids
+ getPost(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) {
+ const postIds = row.postId || this.ids;
+ this.$confirm('是否确认删除岗位编号为"' + postIds + '"的数据项?', "警告", {
+ confirmButtonText: "确定",
+ cancelButtonText: "取消",
+ type: "warning"
+ }).then(function() {
+ return delPost(postIds);
+ }).then(() => {
+ this.getList();
+ this.msgSuccess("删除成功");
+ }).catch(function() {});
+ },
+ /** 导出按钮操作 */
+ handleExport() {
+ this.download('system/post/export', {
+ ...this.queryParams
+ }, `post_${new Date().getTime()}.xlsx`)
+ }
+ }
+};
+</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..2b90d7f
--- /dev/null
+++ b/ruoyi-ui/src/views/system/role/index.vue
@@ -0,0 +1,541 @@
+<template>
+ <div class="app-container">
+ <el-form :model="queryParams" ref="queryForm" :inline="true">
+ <el-form-item label="角色名称" prop="roleName">
+ <el-input
+ v-model="queryParams.roleName"
+ placeholder="请输入角色名称"
+ clearable
+ size="small"
+ style="width: 240px"
+ @keyup.enter.native="handleQuery"
+ />
+ </el-form-item>
+ <el-form-item label="权限字符" prop="roleKey">
+ <el-input
+ v-model="queryParams.roleKey"
+ placeholder="请输入权限字符"
+ clearable
+ size="small"
+ style="width: 240px"
+ @keyup.enter.native="handleQuery"
+ />
+ </el-form-item>
+ <el-form-item label="状态" prop="status">
+ <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 icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+ </el-form-item>
+ </el-form>
+
+ <el-row :gutter="10" class="mb8">
+ <el-col :span="1.5">
+ <el-button
+ type="primary"
+ icon="el-icon-plus"
+ size="mini"
+ @click="handleAdd"
+ v-hasPermi="['system:role:add']"
+ >新增</el-button>
+ </el-col>
+ <el-col :span="1.5">
+ <el-button
+ type="success"
+ icon="el-icon-edit"
+ size="mini"
+ :disabled="single"
+ @click="handleUpdate"
+ v-hasPermi="['system:role:edit']"
+ >修改</el-button>
+ </el-col>
+ <el-col :span="1.5">
+ <el-button
+ type="danger"
+ icon="el-icon-delete"
+ size="mini"
+ :disabled="multiple"
+ @click="handleDelete"
+ v-hasPermi="['system:role:remove']"
+ >删除</el-button>
+ </el-col>
+ <el-col :span="1.5">
+ <el-button
+ type="warning"
+ icon="el-icon-download"
+ size="mini"
+ @click="handleExport"
+ v-hasPermi="['system:post:export']"
+ >导出</el-button>
+ </el-col>
+ </el-row>
+
+ <el-table v-loading="loading" :data="roleList" @selection-change="handleSelectionChange">
+ <el-table-column type="selection" width="55" align="center" />
+ <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="150" />
+ <el-table-column label="显示顺序" prop="roleSort" width="100" />
+ <el-table-column label="状态" align="center" width="100">
+ <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>{{ parseTime(scope.row.createTime) }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+ <template slot-scope="scope">
+ <el-button
+ size="mini"
+ type="text"
+ icon="el-icon-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" append-to-body>
+ <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" append-to-body>
+ <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 {
+ name: "Role",
+ data() {
+ return {
+ // 遮罩层
+ loading: true,
+ // 选中数组
+ ids: [],
+ // 非单个禁用
+ single: true,
+ // 非多个禁用
+ multiple: 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.menuOptions = response.menus;
+ this.$refs.menu.setCheckedKeys(response.checkedKeys);
+ });
+ },
+ /** 根据角色ID查询部门树结构 */
+ getRoleDeptTreeselect(roleId) {
+ roleDeptTreeselect(roleId).then(response => {
+ this.deptOptions = response.depts;
+ this.$refs.dept.setCheckedKeys(response.checkedKeys);
+ });
+ },
+ // 角色状态修改
+ 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.menu != undefined) {
+ this.$refs.menu.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();
+ },
+ /** 重置按钮操作 */
+ resetQuery() {
+ this.dateRange = [];
+ this.resetForm("queryForm");
+ this.handleQuery();
+ },
+ // 多选框选中数据
+ handleSelectionChange(selection) {
+ this.ids = selection.map(item => item.roleId)
+ this.single = selection.length!=1
+ this.multiple = !selection.length
+ },
+ /** 新增按钮操作 */
+ handleAdd() {
+ this.reset();
+ this.getMenuTreeselect();
+ this.open = true;
+ this.title = "添加角色";
+ },
+ /** 修改按钮操作 */
+ handleUpdate(row) {
+ this.reset();
+ const roleId = row.roleId || this.ids
+ this.$nextTick(() => {
+ this.getRoleMenuTreeselect(roleId);
+ });
+ getRole(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) {
+ const roleIds = row.roleId || this.ids;
+ this.$confirm('是否确认删除角色编号为"' + roleIds + '"的数据项?', "警告", {
+ confirmButtonText: "确定",
+ cancelButtonText: "取消",
+ type: "warning"
+ }).then(function() {
+ return delRole(roleIds);
+ }).then(() => {
+ this.getList();
+ this.msgSuccess("删除成功");
+ }).catch(function() {});
+ },
+ /** 导出按钮操作 */
+ handleExport() {
+ this.download('system/role/export', {
+ ...this.queryParams
+ }, `role_${new Date().getTime()}.xlsx`)
+ }
+ }
+};
+</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..31de3fc
--- /dev/null
+++ b/ruoyi-ui/src/views/system/user/index.vue
@@ -0,0 +1,658 @@
+<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 :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
+ <el-form-item label="用户名称" prop="userName">
+ <el-input
+ v-model="queryParams.userName"
+ placeholder="请输入用户名称"
+ clearable
+ size="small"
+ style="width: 240px"
+ @keyup.enter.native="handleQuery"
+ />
+ </el-form-item>
+ <el-form-item label="手机号码" prop="phonenumber">
+ <el-input
+ v-model="queryParams.phonenumber"
+ placeholder="请输入手机号码"
+ clearable
+ size="small"
+ style="width: 240px"
+ @keyup.enter.native="handleQuery"
+ />
+ </el-form-item>
+ <el-form-item label="状态" prop="status">
+ <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 icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+ </el-form-item>
+ </el-form>
+
+ <el-row :gutter="10" class="mb8">
+ <el-col :span="1.5">
+ <el-button
+ type="primary"
+ icon="el-icon-plus"
+ size="mini"
+ @click="handleAdd"
+ v-hasPermi="['system:user:add']"
+ >新增</el-button>
+ </el-col>
+ <el-col :span="1.5">
+ <el-button
+ type="success"
+ icon="el-icon-edit"
+ size="mini"
+ :disabled="single"
+ @click="handleUpdate"
+ v-hasPermi="['system:user:edit']"
+ >修改</el-button>
+ </el-col>
+ <el-col :span="1.5">
+ <el-button
+ type="danger"
+ icon="el-icon-delete"
+ size="mini"
+ :disabled="multiple"
+ @click="handleDelete"
+ v-hasPermi="['system:user:remove']"
+ >删除</el-button>
+ </el-col>
+ <el-col :span="1.5">
+ <el-button
+ type="info"
+ icon="el-icon-upload2"
+ size="mini"
+ @click="handleImport"
+ v-hasPermi="['system:user:import']"
+ >导入</el-button>
+ </el-col>
+ <el-col :span="1.5">
+ <el-button
+ type="warning"
+ icon="el-icon-download"
+ size="mini"
+ @click="handleExport"
+ v-hasPermi="['system:user:export']"
+ >导出</el-button>
+ </el-col>
+ </el-row>
+
+ <el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
+ <el-table-column type="selection" width="40" align="center" />
+ <el-table-column label="用户编号" align="center" prop="userId" />
+ <el-table-column label="用户名称" align="center" prop="userName" :show-overflow-tooltip="true" />
+ <el-table-column label="用户昵称" align="center" prop="nickName" :show-overflow-tooltip="true" />
+ <el-table-column label="部门" align="center" prop="dept.deptName" :show-overflow-tooltip="true" />
+ <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>{{ parseTime(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" append-to-body>
+ <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 v-if="form.userId == undefined" 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>
+
+ <!-- 用户导入对话框 -->
+ <el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" append-to-body>
+ <el-upload
+ ref="upload"
+ :limit="1"
+ accept=".xlsx, .xls"
+ :headers="upload.headers"
+ :action="upload.url + '?updateSupport=' + upload.updateSupport"
+ :disabled="upload.isUploading"
+ :on-progress="handleFileUploadProgress"
+ :on-success="handleFileSuccess"
+ :auto-upload="false"
+ drag
+ >
+ <i class="el-icon-upload"></i>
+ <div class="el-upload__text">
+ 将文件拖到此处,或
+ <em>点击上传</em>
+ </div>
+ <div class="el-upload__tip" slot="tip">
+ <el-checkbox v-model="upload.updateSupport" />是否更新已经存在的用户数据
+ <el-link type="info" style="font-size:12px" @click="importTemplate">下载模板</el-link>
+ </div>
+ <div class="el-upload__tip" style="color:red" slot="tip">提示:仅允许导入“xls”或“xlsx”格式文件!</div>
+ </el-upload>
+ <div slot="footer" class="dialog-footer">
+ <el-button type="primary" @click="submitFileForm">确 定</el-button>
+ <el-button @click="upload.open = false">取 消</el-button>
+ </div>
+ </el-dialog>
+ </div>
+</template>
+
+<script>
+import { listUser, getUser, delUser, addUser, updateUser, resetUserPwd, changeUserStatus } from "@/api/system/user";
+import { getToken } from "@/utils/auth";
+import { treeselect } from "@/api/system/dept";
+import Treeselect from "@riophae/vue-treeselect";
+import "@riophae/vue-treeselect/dist/vue-treeselect.css";
+
+export default {
+ name: "User",
+ components: { Treeselect },
+ data() {
+ return {
+ // 遮罩层
+ loading: true,
+ // 选中数组
+ ids: [],
+ // 非单个禁用
+ single: true,
+ // 非多个禁用
+ multiple: 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"
+ },
+ // 用户导入参数
+ upload: {
+ // 是否显示弹出层(用户导入)
+ open: false,
+ // 弹出层标题(用户导入)
+ title: "",
+ // 是否禁用上传
+ isUploading: false,
+ // 是否更新已经存在的用户数据
+ updateSupport: 0,
+ // 设置上传的请求头部
+ headers: { Authorization: "Bearer " + getToken() },
+ // 上传的地址
+ url: process.env.VUE_APP_BASE_API + "/system/user/importData"
+ },
+ // 查询参数
+ 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: [
+ { 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"
+ }
+ ]
+ }
+ };
+ },
+ 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.msg;
+ });
+ },
+ 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();
+ },
+ // 用户状态修改
+ 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: undefined,
+ userName: undefined,
+ nickName: undefined,
+ password: undefined,
+ phonenumber: undefined,
+ email: undefined,
+ sex: undefined,
+ status: "0",
+ remark: undefined,
+ postIds: [],
+ roleIds: []
+ };
+ this.resetForm("form");
+ },
+ /** 搜索按钮操作 */
+ handleQuery() {
+ this.queryParams.page = 1;
+ this.getList();
+ },
+ /** 重置按钮操作 */
+ resetQuery() {
+ this.dateRange = [];
+ this.resetForm("queryForm");
+ this.handleQuery();
+ },
+ // 多选框选中数据
+ handleSelectionChange(selection) {
+ this.ids = selection.map(item => item.userId);
+ this.single = selection.length != 1;
+ this.multiple = !selection.length;
+ },
+ /** 新增按钮操作 */
+ handleAdd() {
+ this.reset();
+ this.getTreeselect();
+ getUser().then(response => {
+ this.postOptions = response.posts;
+ this.roleOptions = response.roles;
+ this.open = true;
+ this.title = "添加用户";
+ this.form.password = this.initPassword;
+ });
+ },
+ /** 修改按钮操作 */
+ handleUpdate(row) {
+ this.reset();
+ this.getTreeselect();
+ const userId = row.userId || this.ids;
+ getUser(userId).then(response => {
+ this.form = response.data;
+ this.postOptions = response.posts;
+ this.roleOptions = response.roles;
+ 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) {
+ const userIds = row.userId || this.ids;
+ this.$confirm('是否确认删除用户编号为"' + userIds + '"的数据项?', "警告", {
+ confirmButtonText: "确定",
+ cancelButtonText: "取消",
+ type: "warning"
+ }).then(function() {
+ return delUser(userIds);
+ }).then(() => {
+ this.getList();
+ this.msgSuccess("删除成功");
+ }).catch(function() {});
+ },
+ /** 导出按钮操作 */
+ handleExport() {
+ this.download('system/user/export', {
+ ...this.queryParams
+ }, `user_${new Date().getTime()}.xlsx`)
+ },
+ /** 导入按钮操作 */
+ handleImport() {
+ this.upload.title = "用户导入";
+ this.upload.open = true;
+ },
+ /** 下载模板操作 */
+ importTemplate() {
+ this.download('system/user/importTemplate', {
+ ...this.queryParams
+ }, `user_${new Date().getTime()}.xlsx`)
+ },
+ // 文件上传中处理
+ handleFileUploadProgress(event, file, fileList) {
+ this.upload.isUploading = true;
+ },
+ // 文件上传成功处理
+ handleFileSuccess(response, file, fileList) {
+ this.upload.open = false;
+ this.upload.isUploading = false;
+ this.$refs.upload.clearFiles();
+ this.$alert(response.msg, "导入结果", { dangerouslyUseHTMLString: true });
+ this.getList();
+ },
+ // 提交上传文件
+ submitFileForm() {
+ this.$refs.upload.submit();
+ }
+ }
+};
+</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..aa8d9e6
--- /dev/null
+++ b/ruoyi-ui/src/views/system/user/profile/index.vue
@@ -0,0 +1,91 @@
+<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">{{ user.createTime }}</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>
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..5446ab5
--- /dev/null
+++ b/ruoyi-ui/src/views/system/user/profile/userAvatar.vue
@@ -0,0 +1,138 @@
+<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" append-to-body>
+ <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 action="#" :http-request="requestUpload" :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;
+ },
+ // 覆盖默认的上传行为
+ requestUpload() {
+ },
+ // 向左旋转
+ 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/CodeTypeDialog.vue b/ruoyi-ui/src/views/tool/build/CodeTypeDialog.vue
new file mode 100644
index 0000000..99f9eb2
--- /dev/null
+++ b/ruoyi-ui/src/views/tool/build/CodeTypeDialog.vue
@@ -0,0 +1,106 @@
+<template>
+ <div>
+ <el-dialog
+ v-bind="$attrs"
+ width="500px"
+ :close-on-click-modal="false"
+ :modal-append-to-body="false"
+ v-on="$listeners"
+ @open="onOpen"
+ @close="onClose"
+ >
+ <el-row :gutter="15">
+ <el-form
+ ref="elForm"
+ :model="formData"
+ :rules="rules"
+ size="medium"
+ label-width="100px"
+ >
+ <el-col :span="24">
+ <el-form-item label="生成类型" prop="type">
+ <el-radio-group v-model="formData.type">
+ <el-radio-button
+ v-for="(item, index) in typeOptions"
+ :key="index"
+ :label="item.value"
+ :disabled="item.disabled"
+ >
+ {{ item.label }}
+ </el-radio-button>
+ </el-radio-group>
+ </el-form-item>
+ <el-form-item v-if="showFileName" label="文件名" prop="fileName">
+ <el-input v-model="formData.fileName" placeholder="请输入文件名" clearable />
+ </el-form-item>
+ </el-col>
+ </el-form>
+ </el-row>
+
+ <div slot="footer">
+ <el-button @click="close">
+ 取消
+ </el-button>
+ <el-button type="primary" @click="handelConfirm">
+ 确定
+ </el-button>
+ </div>
+ </el-dialog>
+ </div>
+</template>
+<script>
+export default {
+ inheritAttrs: false,
+ props: ['showFileName'],
+ data() {
+ return {
+ formData: {
+ fileName: undefined,
+ type: 'file'
+ },
+ rules: {
+ fileName: [{
+ required: true,
+ message: '请输入文件名',
+ trigger: 'blur'
+ }],
+ type: [{
+ required: true,
+ message: '生成类型不能为空',
+ trigger: 'change'
+ }]
+ },
+ typeOptions: [{
+ label: '页面',
+ value: 'file'
+ }, {
+ label: '弹窗',
+ value: 'dialog'
+ }]
+ }
+ },
+ computed: {
+ },
+ watch: {},
+ mounted() {},
+ methods: {
+ onOpen() {
+ if (this.showFileName) {
+ this.formData.fileName = `${+new Date()}.vue`
+ }
+ },
+ onClose() {
+ },
+ close(e) {
+ this.$emit('update:visible', false)
+ },
+ handelConfirm() {
+ this.$refs.elForm.validate(valid => {
+ if (!valid) return
+ this.$emit('confirm', { ...this.formData })
+ this.close()
+ })
+ }
+ }
+}
+</script>
diff --git a/ruoyi-ui/src/views/tool/build/DraggableItem.vue b/ruoyi-ui/src/views/tool/build/DraggableItem.vue
new file mode 100644
index 0000000..f669ac0
--- /dev/null
+++ b/ruoyi-ui/src/views/tool/build/DraggableItem.vue
@@ -0,0 +1,100 @@
+<script>
+import draggable from 'vuedraggable'
+import render from '@/utils/generator/render'
+
+const components = {
+ itemBtns(h, element, index, parent) {
+ const { copyItem, deleteItem } = this.$listeners
+ return [
+ <span class="drawing-item-copy" title="复制" onClick={event => {
+ copyItem(element, parent); event.stopPropagation()
+ }}>
+ <i class="el-icon-copy-document" />
+ </span>,
+ <span class="drawing-item-delete" title="删除" onClick={event => {
+ deleteItem(index, parent); event.stopPropagation()
+ }}>
+ <i class="el-icon-delete" />
+ </span>
+ ]
+ }
+}
+const layouts = {
+ colFormItem(h, element, index, parent) {
+ const { activeItem } = this.$listeners
+ let className = this.activeId === element.formId ? 'drawing-item active-from-item' : 'drawing-item'
+ if (this.formConf.unFocusedComponentBorder) className += ' unfocus-bordered'
+ return (
+ <el-col span={element.span} class={className}
+ nativeOnClick={event => { activeItem(element); event.stopPropagation() }}>
+ <el-form-item label-width={element.labelWidth ? `${element.labelWidth}px` : null}
+ label={element.label} required={element.required}>
+ <render key={element.renderKey} conf={element} onInput={ event => {
+ this.$set(element, 'defaultValue', event)
+ }} />
+ </el-form-item>
+ {components.itemBtns.apply(this, arguments)}
+ </el-col>
+ )
+ },
+ rowFormItem(h, element, index, parent) {
+ const { activeItem } = this.$listeners
+ const className = this.activeId === element.formId ? 'drawing-row-item active-from-item' : 'drawing-row-item'
+ let child = renderChildren.apply(this, arguments)
+ if (element.type === 'flex') {
+ child = <el-row type={element.type} justify={element.justify} align={element.align}>
+ {child}
+ </el-row>
+ }
+ return (
+ <el-col span={element.span}>
+ <el-row gutter={element.gutter} class={className}
+ nativeOnClick={event => { activeItem(element); event.stopPropagation() }}>
+ <span class="component-name">{element.componentName}</span>
+ <draggable list={element.children} animation={340} group="componentsGroup" class="drag-wrapper">
+ {child}
+ </draggable>
+ {components.itemBtns.apply(this, arguments)}
+ </el-row>
+ </el-col>
+ )
+ }
+}
+
+function renderChildren(h, element, index, parent) {
+ if (!Array.isArray(element.children)) return null
+ return element.children.map((el, i) => {
+ const layout = layouts[el.layout]
+ if (layout) {
+ return layout.call(this, h, el, i, element.children)
+ }
+ return layoutIsNotFound()
+ })
+}
+
+function layoutIsNotFound() {
+ throw new Error(`没有与${this.element.layout}匹配的layout`)
+}
+
+export default {
+ components: {
+ render,
+ draggable
+ },
+ props: [
+ 'element',
+ 'index',
+ 'drawingList',
+ 'activeId',
+ 'formConf'
+ ],
+ render(h) {
+ const layout = layouts[this.element.layout]
+
+ if (layout) {
+ return layout.call(this, h, this.element, this.index, this.drawingList)
+ }
+ return layoutIsNotFound()
+ }
+}
+</script>
diff --git a/ruoyi-ui/src/views/tool/build/IconsDialog.vue b/ruoyi-ui/src/views/tool/build/IconsDialog.vue
new file mode 100644
index 0000000..0d12778
--- /dev/null
+++ b/ruoyi-ui/src/views/tool/build/IconsDialog.vue
@@ -0,0 +1,123 @@
+<template>
+ <div class="icon-dialog">
+ <el-dialog
+ v-bind="$attrs"
+ width="980px"
+ :modal-append-to-body="false"
+ v-on="$listeners"
+ @open="onOpen"
+ @close="onClose"
+ >
+ <div slot="title">
+ 选择图标
+ <el-input
+ v-model="key"
+ size="mini"
+ :style="{width: '260px'}"
+ placeholder="请输入图标名称"
+ prefix-icon="el-icon-search"
+ clearable
+ />
+ </div>
+ <ul class="icon-ul">
+ <li
+ v-for="icon in iconList"
+ :key="icon"
+ :class="active===icon?'active-item':''"
+ @click="onSelect(icon)"
+ >
+ <i :class="icon" />
+ <div>{{ icon }}</div>
+ </li>
+ </ul>
+ </el-dialog>
+ </div>
+</template>
+<script>
+import iconList from '@/utils/generator/icon.json'
+
+const originList = iconList.map(name => `el-icon-${name}`)
+
+export default {
+ inheritAttrs: false,
+ props: ['current'],
+ data() {
+ return {
+ iconList: originList,
+ active: null,
+ key: ''
+ }
+ },
+ watch: {
+ key(val) {
+ if (val) {
+ this.iconList = originList.filter(name => name.indexOf(val) > -1)
+ } else {
+ this.iconList = originList
+ }
+ }
+ },
+ methods: {
+ onOpen() {
+ this.active = this.current
+ this.key = ''
+ },
+ onClose() {},
+ onSelect(icon) {
+ this.active = icon
+ this.$emit('select', icon)
+ this.$emit('update:visible', false)
+ }
+ }
+}
+</script>
+<style lang="scss" scoped>
+.icon-ul {
+ margin: 0;
+ padding: 0;
+ font-size: 0;
+ li {
+ list-style-type: none;
+ text-align: center;
+ font-size: 14px;
+ display: inline-block;
+ width: 16.66%;
+ box-sizing: border-box;
+ height: 108px;
+ padding: 15px 6px 6px 6px;
+ cursor: pointer;
+ overflow: hidden;
+ &:hover {
+ background: #f2f2f2;
+ }
+ &.active-item{
+ background: #e1f3fb;
+ color: #7a6df0
+ }
+ > i {
+ font-size: 30px;
+ line-height: 50px;
+ }
+ }
+}
+.icon-dialog {
+ ::v-deep .el-dialog {
+ border-radius: 8px;
+ margin-bottom: 0;
+ margin-top: 4vh !important;
+ display: flex;
+ flex-direction: column;
+ max-height: 92vh;
+ overflow: hidden;
+ box-sizing: border-box;
+ .el-dialog__header {
+ padding-top: 14px;
+ }
+ .el-dialog__body {
+ margin: 0 20px 20px 20px;
+ padding: 0;
+ overflow: auto;
+ }
+ }
+}
+</style>
diff --git a/ruoyi-ui/src/views/tool/build/RightPanel.vue b/ruoyi-ui/src/views/tool/build/RightPanel.vue
new file mode 100644
index 0000000..1acdc57
--- /dev/null
+++ b/ruoyi-ui/src/views/tool/build/RightPanel.vue
@@ -0,0 +1,944 @@
+<template>
+ <div class="right-board">
+ <el-tabs v-model="currentTab" class="center-tabs">
+ <el-tab-pane label="组件属性" name="field" />
+ <el-tab-pane label="表单属性" name="form" />
+ </el-tabs>
+ <div class="field-box">
+ <a class="document-link" target="_blank" :href="documentLink" title="查看组件文档">
+ <i class="el-icon-link" />
+ </a>
+ <el-scrollbar class="right-scrollbar">
+ <!-- 组件属性 -->
+ <el-form v-show="currentTab==='field' && showField" size="small" label-width="90px">
+ <el-form-item v-if="activeData.changeTag" label="组件类型">
+ <el-select
+ v-model="activeData.tagIcon"
+ placeholder="请选择组件类型"
+ :style="{width: '100%'}"
+ @change="tagChange"
+ >
+ <el-option-group v-for="group in tagList" :key="group.label" :label="group.label">
+ <el-option
+ v-for="item in group.options"
+ :key="item.label"
+ :label="item.label"
+ :value="item.tagIcon"
+ >
+ <svg-icon class="node-icon" :icon-class="item.tagIcon" />
+ <span> {{ item.label }}</span>
+ </el-option>
+ </el-option-group>
+ </el-select>
+ </el-form-item>
+ <el-form-item v-if="activeData.vModel!==undefined" label="字段名">
+ <el-input v-model="activeData.vModel" placeholder="请输入字段名(v-model)" />
+ </el-form-item>
+ <el-form-item v-if="activeData.componentName!==undefined" label="组件名">
+ {{ activeData.componentName }}
+ </el-form-item>
+ <el-form-item v-if="activeData.label!==undefined" label="标题">
+ <el-input v-model="activeData.label" placeholder="请输入标题" />
+ </el-form-item>
+ <el-form-item v-if="activeData.placeholder!==undefined" label="占位提示">
+ <el-input v-model="activeData.placeholder" placeholder="请输入占位提示" />
+ </el-form-item>
+ <el-form-item v-if="activeData['start-placeholder']!==undefined" label="开始占位">
+ <el-input v-model="activeData['start-placeholder']" placeholder="请输入占位提示" />
+ </el-form-item>
+ <el-form-item v-if="activeData['end-placeholder']!==undefined" label="结束占位">
+ <el-input v-model="activeData['end-placeholder']" placeholder="请输入占位提示" />
+ </el-form-item>
+ <el-form-item v-if="activeData.span!==undefined" label="表单栅格">
+ <el-slider v-model="activeData.span" :max="24" :min="1" :marks="{12:''}" @change="spanChange" />
+ </el-form-item>
+ <el-form-item v-if="activeData.layout==='rowFormItem'" label="栅格间隔">
+ <el-input-number v-model="activeData.gutter" :min="0" placeholder="栅格间隔" />
+ </el-form-item>
+ <el-form-item v-if="activeData.layout==='rowFormItem'" label="布局模式">
+ <el-radio-group v-model="activeData.type">
+ <el-radio-button label="default" />
+ <el-radio-button label="flex" />
+ </el-radio-group>
+ </el-form-item>
+ <el-form-item v-if="activeData.justify!==undefined&&activeData.type==='flex'" label="水平排列">
+ <el-select v-model="activeData.justify" placeholder="请选择水平排列" :style="{width: '100%'}">
+ <el-option
+ v-for="(item, index) in justifyOptions"
+ :key="index"
+ :label="item.label"
+ :value="item.value"
+ />
+ </el-select>
+ </el-form-item>
+ <el-form-item v-if="activeData.align!==undefined&&activeData.type==='flex'" label="垂直排列">
+ <el-radio-group v-model="activeData.align">
+ <el-radio-button label="top" />
+ <el-radio-button label="middle" />
+ <el-radio-button label="bottom" />
+ </el-radio-group>
+ </el-form-item>
+ <el-form-item v-if="activeData.labelWidth!==undefined" label="标签宽度">
+ <el-input v-model.number="activeData.labelWidth" type="number" placeholder="请输入标签宽度" />
+ </el-form-item>
+ <el-form-item v-if="activeData.style&&activeData.style.width!==undefined" label="组件宽度">
+ <el-input v-model="activeData.style.width" placeholder="请输入组件宽度" clearable />
+ </el-form-item>
+ <el-form-item v-if="activeData.vModel!==undefined" label="默认值">
+ <el-input
+ :value="setDefaultValue(activeData.defaultValue)"
+ placeholder="请输入默认值"
+ @input="onDefaultValueInput"
+ />
+ </el-form-item>
+ <el-form-item v-if="activeData.tag==='el-checkbox-group'" label="至少应选">
+ <el-input-number
+ :value="activeData.min"
+ :min="0"
+ placeholder="至少应选"
+ @input="$set(activeData, 'min', $event?$event:undefined)"
+ />
+ </el-form-item>
+ <el-form-item v-if="activeData.tag==='el-checkbox-group'" label="最多可选">
+ <el-input-number
+ :value="activeData.max"
+ :min="0"
+ placeholder="最多可选"
+ @input="$set(activeData, 'max', $event?$event:undefined)"
+ />
+ </el-form-item>
+ <el-form-item v-if="activeData.prepend!==undefined" label="前缀">
+ <el-input v-model="activeData.prepend" placeholder="请输入前缀" />
+ </el-form-item>
+ <el-form-item v-if="activeData.append!==undefined" label="后缀">
+ <el-input v-model="activeData.append" placeholder="请输入后缀" />
+ </el-form-item>
+ <el-form-item v-if="activeData['prefix-icon']!==undefined" label="前图标">
+ <el-input v-model="activeData['prefix-icon']" placeholder="请输入前图标名称">
+ <el-button slot="append" icon="el-icon-thumb" @click="openIconsDialog('prefix-icon')">
+ 选择
+ </el-button>
+ </el-input>
+ </el-form-item>
+ <el-form-item v-if="activeData['suffix-icon'] !== undefined" label="后图标">
+ <el-input v-model="activeData['suffix-icon']" placeholder="请输入后图标名称">
+ <el-button slot="append" icon="el-icon-thumb" @click="openIconsDialog('suffix-icon')">
+ 选择
+ </el-button>
+ </el-input>
+ </el-form-item>
+ <el-form-item v-if="activeData.tag === 'el-cascader'" label="选项分隔符">
+ <el-input v-model="activeData.separator" placeholder="请输入选项分隔符" />
+ </el-form-item>
+ <el-form-item v-if="activeData.autosize !== undefined" label="最小行数">
+ <el-input-number v-model="activeData.autosize.minRows" :min="1" placeholder="最小行数" />
+ </el-form-item>
+ <el-form-item v-if="activeData.autosize !== undefined" label="最大行数">
+ <el-input-number v-model="activeData.autosize.maxRows" :min="1" placeholder="最大行数" />
+ </el-form-item>
+ <el-form-item v-if="activeData.min !== undefined" label="最小值">
+ <el-input-number v-model="activeData.min" placeholder="最小值" />
+ </el-form-item>
+ <el-form-item v-if="activeData.max !== undefined" label="最大值">
+ <el-input-number v-model="activeData.max" placeholder="最大值" />
+ </el-form-item>
+ <el-form-item v-if="activeData.step !== undefined" label="步长">
+ <el-input-number v-model="activeData.step" placeholder="步数" />
+ </el-form-item>
+ <el-form-item v-if="activeData.tag === 'el-input-number'" label="精度">
+ <el-input-number v-model="activeData.precision" :min="0" placeholder="精度" />
+ </el-form-item>
+ <el-form-item v-if="activeData.tag === 'el-input-number'" label="按钮位置">
+ <el-radio-group v-model="activeData['controls-position']">
+ <el-radio-button label="">
+ 默认
+ </el-radio-button>
+ <el-radio-button label="right">
+ 右侧
+ </el-radio-button>
+ </el-radio-group>
+ </el-form-item>
+ <el-form-item v-if="activeData.maxlength !== undefined" label="最多输入">
+ <el-input v-model="activeData.maxlength" placeholder="请输入字符长度">
+ <template slot="append">
+ 个字符
+ </template>
+ </el-input>
+ </el-form-item>
+ <el-form-item v-if="activeData['active-text'] !== undefined" label="开启提示">
+ <el-input v-model="activeData['active-text']" placeholder="请输入开启提示" />
+ </el-form-item>
+ <el-form-item v-if="activeData['inactive-text'] !== undefined" label="关闭提示">
+ <el-input v-model="activeData['inactive-text']" placeholder="请输入关闭提示" />
+ </el-form-item>
+ <el-form-item v-if="activeData['active-value'] !== undefined" label="开启值">
+ <el-input
+ :value="setDefaultValue(activeData['active-value'])"
+ placeholder="请输入开启值"
+ @input="onSwitchValueInput($event, 'active-value')"
+ />
+ </el-form-item>
+ <el-form-item v-if="activeData['inactive-value'] !== undefined" label="关闭值">
+ <el-input
+ :value="setDefaultValue(activeData['inactive-value'])"
+ placeholder="请输入关闭值"
+ @input="onSwitchValueInput($event, 'inactive-value')"
+ />
+ </el-form-item>
+ <el-form-item
+ v-if="activeData.type !== undefined && 'el-date-picker' === activeData.tag"
+ label="时间类型"
+ >
+ <el-select
+ v-model="activeData.type"
+ placeholder="请选择时间类型"
+ :style="{ width: '100%' }"
+ @change="dateTypeChange"
+ >
+ <el-option
+ v-for="(item, index) in dateOptions"
+ :key="index"
+ :label="item.label"
+ :value="item.value"
+ />
+ </el-select>
+ </el-form-item>
+ <el-form-item v-if="activeData.name !== undefined" label="文件字段名">
+ <el-input v-model="activeData.name" placeholder="请输入上传文件字段名" />
+ </el-form-item>
+ <el-form-item v-if="activeData.accept !== undefined" label="文件类型">
+ <el-select
+ v-model="activeData.accept"
+ placeholder="请选择文件类型"
+ :style="{ width: '100%' }"
+ clearable
+ >
+ <el-option label="图片" value="image/*" />
+ <el-option label="视频" value="video/*" />
+ <el-option label="音频" value="audio/*" />
+ <el-option label="excel" value=".xls,.xlsx" />
+ <el-option label="word" value=".doc,.docx" />
+ <el-option label="pdf" value=".pdf" />
+ <el-option label="txt" value=".txt" />
+ </el-select>
+ </el-form-item>
+ <el-form-item v-if="activeData.fileSize !== undefined" label="文件大小">
+ <el-input v-model.number="activeData.fileSize" placeholder="请输入文件大小">
+ <el-select slot="append" v-model="activeData.sizeUnit" :style="{ width: '66px' }">
+ <el-option label="KB" value="KB" />
+ <el-option label="MB" value="MB" />
+ <el-option label="GB" value="GB" />
+ </el-select>
+ </el-input>
+ </el-form-item>
+ <el-form-item v-if="activeData.action !== undefined" label="上传地址">
+ <el-input v-model="activeData.action" placeholder="请输入上传地址" clearable />
+ </el-form-item>
+ <el-form-item v-if="activeData['list-type'] !== undefined" label="列表类型">
+ <el-radio-group v-model="activeData['list-type']" size="small">
+ <el-radio-button label="text">
+ text
+ </el-radio-button>
+ <el-radio-button label="picture">
+ picture
+ </el-radio-button>
+ <el-radio-button label="picture-card">
+ picture-card
+ </el-radio-button>
+ </el-radio-group>
+ </el-form-item>
+ <el-form-item
+ v-if="activeData.buttonText !== undefined"
+ v-show="'picture-card' !== activeData['list-type']"
+ label="按钮文字"
+ >
+ <el-input v-model="activeData.buttonText" placeholder="请输入按钮文字" />
+ </el-form-item>
+ <el-form-item v-if="activeData['range-separator'] !== undefined" label="分隔符">
+ <el-input v-model="activeData['range-separator']" placeholder="请输入分隔符" />
+ </el-form-item>
+ <el-form-item v-if="activeData['picker-options'] !== undefined" label="时间段">
+ <el-input
+ v-model="activeData['picker-options'].selectableRange"
+ placeholder="请输入时间段"
+ />
+ </el-form-item>
+ <el-form-item v-if="activeData.format !== undefined" label="时间格式">
+ <el-input
+ :value="activeData.format"
+ placeholder="请输入时间格式"
+ @input="setTimeValue($event)"
+ />
+ </el-form-item>
+ <template v-if="['el-checkbox-group', 'el-radio-group', 'el-select'].indexOf(activeData.tag) > -1">
+ <el-divider>选项</el-divider>
+ <draggable
+ :list="activeData.options"
+ :animation="340"
+ group="selectItem"
+ handle=".option-drag"
+ >
+ <div v-for="(item, index) in activeData.options" :key="index" class="select-item">
+ <div class="select-line-icon option-drag">
+ <i class="el-icon-s-operation" />
+ </div>
+ <el-input v-model="item.label" placeholder="选项名" size="small" />
+ <el-input
+ placeholder="选项值"
+ size="small"
+ :value="item.value"
+ @input="setOptionValue(item, $event)"
+ />
+ <div class="close-btn select-line-icon" @click="activeData.options.splice(index, 1)">
+ <i class="el-icon-remove-outline" />
+ </div>
+ </div>
+ </draggable>
+ <div style="margin-left: 20px;">
+ <el-button
+ style="padding-bottom: 0"
+ icon="el-icon-circle-plus-outline"
+ type="text"
+ @click="addSelectItem"
+ >
+ 添加选项
+ </el-button>
+ </div>
+ <el-divider />
+ </template>
+
+ <template v-if="['el-cascader'].indexOf(activeData.tag) > -1">
+ <el-divider>选项</el-divider>
+ <el-form-item label="数据类型">
+ <el-radio-group v-model="activeData.dataType" size="small">
+ <el-radio-button label="dynamic">
+ 动态数据
+ </el-radio-button>
+ <el-radio-button label="static">
+ 静态数据
+ </el-radio-button>
+ </el-radio-group>
+ </el-form-item>
+
+ <template v-if="activeData.dataType === 'dynamic'">
+ <el-form-item label="标签键名">
+ <el-input v-model="activeData.labelKey" placeholder="请输入标签键名" />
+ </el-form-item>
+ <el-form-item label="值键名">
+ <el-input v-model="activeData.valueKey" placeholder="请输入值键名" />
+ </el-form-item>
+ <el-form-item label="子级键名">
+ <el-input v-model="activeData.childrenKey" placeholder="请输入子级键名" />
+ </el-form-item>
+ </template>
+
+ <el-tree
+ v-if="activeData.dataType === 'static'"
+ draggable
+ :data="activeData.options"
+ node-key="id"
+ :expand-on-click-node="false"
+ :render-content="renderContent"
+ />
+ <div v-if="activeData.dataType === 'static'" style="margin-left: 20px">
+ <el-button
+ style="padding-bottom: 0"
+ icon="el-icon-circle-plus-outline"
+ type="text"
+ @click="addTreeItem"
+ >
+ 添加父级
+ </el-button>
+ </div>
+ <el-divider />
+ </template>
+
+ <el-form-item v-if="activeData.optionType !== undefined" label="选项样式">
+ <el-radio-group v-model="activeData.optionType">
+ <el-radio-button label="default">
+ 默认
+ </el-radio-button>
+ <el-radio-button label="button">
+ 按钮
+ </el-radio-button>
+ </el-radio-group>
+ </el-form-item>
+ <el-form-item v-if="activeData['active-color'] !== undefined" label="开启颜色">
+ <el-color-picker v-model="activeData['active-color']" />
+ </el-form-item>
+ <el-form-item v-if="activeData['inactive-color'] !== undefined" label="关闭颜色">
+ <el-color-picker v-model="activeData['inactive-color']" />
+ </el-form-item>
+
+ <el-form-item v-if="activeData['allow-half'] !== undefined" label="允许半选">
+ <el-switch v-model="activeData['allow-half']" />
+ </el-form-item>
+ <el-form-item v-if="activeData['show-text'] !== undefined" label="辅助文字">
+ <el-switch v-model="activeData['show-text']" @change="rateTextChange" />
+ </el-form-item>
+ <el-form-item v-if="activeData['show-score'] !== undefined" label="显示分数">
+ <el-switch v-model="activeData['show-score']" @change="rateScoreChange" />
+ </el-form-item>
+ <el-form-item v-if="activeData['show-stops'] !== undefined" label="显示间断点">
+ <el-switch v-model="activeData['show-stops']" />
+ </el-form-item>
+ <el-form-item v-if="activeData.range !== undefined" label="范围选择">
+ <el-switch v-model="activeData.range" @change="rangeChange" />
+ </el-form-item>
+ <el-form-item
+ v-if="activeData.border !== undefined && activeData.optionType === 'default'"
+ label="是否带边框"
+ >
+ <el-switch v-model="activeData.border" />
+ </el-form-item>
+ <el-form-item v-if="activeData.tag === 'el-color-picker'" label="颜色格式">
+ <el-select
+ v-model="activeData['color-format']"
+ placeholder="请选择颜色格式"
+ :style="{ width: '100%' }"
+ @change="colorFormatChange"
+ >
+ <el-option
+ v-for="(item, index) in colorFormatOptions"
+ :key="index"
+ :label="item.label"
+ :value="item.value"
+ />
+ </el-select>
+ </el-form-item>
+ <el-form-item
+ v-if="activeData.size !== undefined &&
+ (activeData.optionType === 'button' ||
+ activeData.border ||
+ activeData.tag === 'el-color-picker')"
+ label="选项尺寸"
+ >
+ <el-radio-group v-model="activeData.size">
+ <el-radio-button label="medium">
+ 中等
+ </el-radio-button>
+ <el-radio-button label="small">
+ 较小
+ </el-radio-button>
+ <el-radio-button label="mini">
+ 迷你
+ </el-radio-button>
+ </el-radio-group>
+ </el-form-item>
+ <el-form-item v-if="activeData['show-word-limit'] !== undefined" label="输入统计">
+ <el-switch v-model="activeData['show-word-limit']" />
+ </el-form-item>
+ <el-form-item v-if="activeData.tag === 'el-input-number'" label="严格步数">
+ <el-switch v-model="activeData['step-strictly']" />
+ </el-form-item>
+ <el-form-item v-if="activeData.tag === 'el-cascader'" label="是否多选">
+ <el-switch v-model="activeData.props.props.multiple" />
+ </el-form-item>
+ <el-form-item v-if="activeData.tag === 'el-cascader'" label="展示全路径">
+ <el-switch v-model="activeData['show-all-levels']" />
+ </el-form-item>
+ <el-form-item v-if="activeData.tag === 'el-cascader'" label="可否筛选">
+ <el-switch v-model="activeData.filterable" />
+ </el-form-item>
+ <el-form-item v-if="activeData.clearable !== undefined" label="能否清空">
+ <el-switch v-model="activeData.clearable" />
+ </el-form-item>
+ <el-form-item v-if="activeData.showTip !== undefined" label="显示提示">
+ <el-switch v-model="activeData.showTip" />
+ </el-form-item>
+ <el-form-item v-if="activeData.multiple !== undefined" label="多选文件">
+ <el-switch v-model="activeData.multiple" />
+ </el-form-item>
+ <el-form-item v-if="activeData['auto-upload'] !== undefined" label="自动上传">
+ <el-switch v-model="activeData['auto-upload']" />
+ </el-form-item>
+ <el-form-item v-if="activeData.readonly !== undefined" label="是否只读">
+ <el-switch v-model="activeData.readonly" />
+ </el-form-item>
+ <el-form-item v-if="activeData.disabled !== undefined" label="是否禁用">
+ <el-switch v-model="activeData.disabled" />
+ </el-form-item>
+ <el-form-item v-if="activeData.tag === 'el-select'" label="是否可搜索">
+ <el-switch v-model="activeData.filterable" />
+ </el-form-item>
+ <el-form-item v-if="activeData.tag === 'el-select'" label="是否多选">
+ <el-switch v-model="activeData.multiple" @change="multipleChange" />
+ </el-form-item>
+ <el-form-item v-if="activeData.required !== undefined" label="是否必填">
+ <el-switch v-model="activeData.required" />
+ </el-form-item>
+
+ <template v-if="activeData.layoutTree">
+ <el-divider>布局结构树</el-divider>
+ <el-tree
+ :data="[activeData]"
+ :props="layoutTreeProps"
+ node-key="renderKey"
+ default-expand-all
+ draggable
+ >
+ <span slot-scope="{ node, data }">
+ <span class="node-label">
+ <svg-icon class="node-icon" :icon-class="data.tagIcon" />
+ {{ node.label }}
+ </span>
+ </span>
+ </el-tree>
+ </template>
+
+ <template v-if="activeData.layout === 'colFormItem'">
+ <el-divider>正则校验</el-divider>
+ <div
+ v-for="(item, index) in activeData.regList"
+ :key="index"
+ class="reg-item"
+ >
+ <span class="close-btn" @click="activeData.regList.splice(index, 1)">
+ <i class="el-icon-close" />
+ </span>
+ <el-form-item label="表达式">
+ <el-input v-model="item.pattern" placeholder="请输入正则" />
+ </el-form-item>
+ <el-form-item label="错误提示" style="margin-bottom:0">
+ <el-input v-model="item.message" placeholder="请输入错误提示" />
+ </el-form-item>
+ </div>
+ <div style="margin-left: 20px">
+ <el-button icon="el-icon-circle-plus-outline" type="text" @click="addReg">
+ 添加规则
+ </el-button>
+ </div>
+ </template>
+ </el-form>
+ <!-- 表单属性 -->
+ <el-form v-show="currentTab === 'form'" size="small" label-width="90px">
+ <el-form-item label="表单名">
+ <el-input v-model="formConf.formRef" placeholder="请输入表单名(ref)" />
+ </el-form-item>
+ <el-form-item label="表单模型">
+ <el-input v-model="formConf.formModel" placeholder="请输入数据模型" />
+ </el-form-item>
+ <el-form-item label="校验模型">
+ <el-input v-model="formConf.formRules" placeholder="请输入校验模型" />
+ </el-form-item>
+ <el-form-item label="表单尺寸">
+ <el-radio-group v-model="formConf.size">
+ <el-radio-button label="medium">
+ 中等
+ </el-radio-button>
+ <el-radio-button label="small">
+ 较小
+ </el-radio-button>
+ <el-radio-button label="mini">
+ 迷你
+ </el-radio-button>
+ </el-radio-group>
+ </el-form-item>
+ <el-form-item label="标签对齐">
+ <el-radio-group v-model="formConf.labelPosition">
+ <el-radio-button label="left">
+ 左对齐
+ </el-radio-button>
+ <el-radio-button label="right">
+ 右对齐
+ </el-radio-button>
+ <el-radio-button label="top">
+ 顶部对齐
+ </el-radio-button>
+ </el-radio-group>
+ </el-form-item>
+ <el-form-item label="标签宽度">
+ <el-input-number v-model="formConf.labelWidth" placeholder="标签宽度" />
+ </el-form-item>
+ <el-form-item label="栅格间隔">
+ <el-input-number v-model="formConf.gutter" :min="0" placeholder="栅格间隔" />
+ </el-form-item>
+ <el-form-item label="禁用表单">
+ <el-switch v-model="formConf.disabled" />
+ </el-form-item>
+ <el-form-item label="表单按钮">
+ <el-switch v-model="formConf.formBtns" />
+ </el-form-item>
+ <el-form-item label="显示未选中组件边框">
+ <el-switch v-model="formConf.unFocusedComponentBorder" />
+ </el-form-item>
+ </el-form>
+ </el-scrollbar>
+ </div>
+
+ <treeNode-dialog :visible.sync="dialogVisible" title="添加选项" @commit="addNode" />
+ <icons-dialog :visible.sync="iconsVisible" :current="activeData[currentIconModel]" @select="setIcon" />
+ </div>
+</template>
+
+<script>
+import { isArray } from 'util'
+import TreeNodeDialog from './TreeNodeDialog'
+import { isNumberStr } from '@/utils/index'
+import IconsDialog from './IconsDialog'
+import {
+ inputComponents,
+ selectComponents,
+ layoutComponents
+} from '@/utils/generator/config'
+
+const dateTimeFormat = {
+ date: 'yyyy-MM-dd',
+ week: 'yyyy 第 WW 周',
+ month: 'yyyy-MM',
+ year: 'yyyy',
+ datetime: 'yyyy-MM-dd HH:mm:ss',
+ daterange: 'yyyy-MM-dd',
+ monthrange: 'yyyy-MM',
+ datetimerange: 'yyyy-MM-dd HH:mm:ss'
+}
+
+export default {
+ components: {
+ TreeNodeDialog,
+ IconsDialog
+ },
+ props: ['showField', 'activeData', 'formConf'],
+ data() {
+ return {
+ currentTab: 'field',
+ currentNode: null,
+ dialogVisible: false,
+ iconsVisible: false,
+ currentIconModel: null,
+ dateTypeOptions: [
+ {
+ label: '日(date)',
+ value: 'date'
+ },
+ {
+ label: '周(week)',
+ value: 'week'
+ },
+ {
+ label: '月(month)',
+ value: 'month'
+ },
+ {
+ label: '年(year)',
+ value: 'year'
+ },
+ {
+ label: '日期时间(datetime)',
+ value: 'datetime'
+ }
+ ],
+ dateRangeTypeOptions: [
+ {
+ label: '日期范围(daterange)',
+ value: 'daterange'
+ },
+ {
+ label: '月范围(monthrange)',
+ value: 'monthrange'
+ },
+ {
+ label: '日期时间范围(datetimerange)',
+ value: 'datetimerange'
+ }
+ ],
+ colorFormatOptions: [
+ {
+ label: 'hex',
+ value: 'hex'
+ },
+ {
+ label: 'rgb',
+ value: 'rgb'
+ },
+ {
+ label: 'rgba',
+ value: 'rgba'
+ },
+ {
+ label: 'hsv',
+ value: 'hsv'
+ },
+ {
+ label: 'hsl',
+ value: 'hsl'
+ }
+ ],
+ justifyOptions: [
+ {
+ label: 'start',
+ value: 'start'
+ },
+ {
+ label: 'end',
+ value: 'end'
+ },
+ {
+ label: 'center',
+ value: 'center'
+ },
+ {
+ label: 'space-around',
+ value: 'space-around'
+ },
+ {
+ label: 'space-between',
+ value: 'space-between'
+ }
+ ],
+ layoutTreeProps: {
+ label(data, node) {
+ return data.componentName || `${data.label}: ${data.vModel}`
+ }
+ }
+ }
+ },
+ computed: {
+ documentLink() {
+ return (
+ this.activeData.document
+ || 'https://element.eleme.cn/#/zh-CN/component/installation'
+ )
+ },
+ dateOptions() {
+ if (
+ this.activeData.type !== undefined
+ && this.activeData.tag === 'el-date-picker'
+ ) {
+ if (this.activeData['start-placeholder'] === undefined) {
+ return this.dateTypeOptions
+ }
+ return this.dateRangeTypeOptions
+ }
+ return []
+ },
+ tagList() {
+ return [
+ {
+ label: '输入型组件',
+ options: inputComponents
+ },
+ {
+ label: '选择型组件',
+ options: selectComponents
+ }
+ ]
+ }
+ },
+ methods: {
+ addReg() {
+ this.activeData.regList.push({
+ pattern: '',
+ message: ''
+ })
+ },
+ addSelectItem() {
+ this.activeData.options.push({
+ label: '',
+ value: ''
+ })
+ },
+ addTreeItem() {
+ ++this.idGlobal
+ this.dialogVisible = true
+ this.currentNode = this.activeData.options
+ },
+ renderContent(h, { node, data, store }) {
+ return (
+ <div class="custom-tree-node">
+ <span>{node.label}</span>
+ <span class="node-operation">
+ <i on-click={() => this.append(data)}
+ class="el-icon-plus"
+ title="添加"
+ ></i>
+ <i on-click={() => this.remove(node, data)}
+ class="el-icon-delete"
+ title="删除"
+ ></i>
+ </span>
+ </div>
+ )
+ },
+ append(data) {
+ if (!data.children) {
+ this.$set(data, 'children', [])
+ }
+ this.dialogVisible = true
+ this.currentNode = data.children
+ },
+ remove(node, data) {
+ const { parent } = node
+ const children = parent.data.children || parent.data
+ const index = children.findIndex(d => d.id === data.id)
+ children.splice(index, 1)
+ },
+ addNode(data) {
+ this.currentNode.push(data)
+ },
+ setOptionValue(item, val) {
+ item.value = isNumberStr(val) ? +val : val
+ },
+ setDefaultValue(val) {
+ if (Array.isArray(val)) {
+ return val.join(',')
+ }
+ if (['string', 'number'].indexOf(val) > -1) {
+ return val
+ }
+ if (typeof val === 'boolean') {
+ return `${val}`
+ }
+ return val
+ },
+ onDefaultValueInput(str) {
+ if (isArray(this.activeData.defaultValue)) {
+ // 数组
+ this.$set(
+ this.activeData,
+ 'defaultValue',
+ str.split(',').map(val => (isNumberStr(val) ? +val : val))
+ )
+ } else if (['true', 'false'].indexOf(str) > -1) {
+ // 布尔
+ this.$set(this.activeData, 'defaultValue', JSON.parse(str))
+ } else {
+ // 字符串和数字
+ this.$set(
+ this.activeData,
+ 'defaultValue',
+ isNumberStr(str) ? +str : str
+ )
+ }
+ },
+ onSwitchValueInput(val, name) {
+ if (['true', 'false'].indexOf(val) > -1) {
+ this.$set(this.activeData, name, JSON.parse(val))
+ } else {
+ this.$set(this.activeData, name, isNumberStr(val) ? +val : val)
+ }
+ },
+ setTimeValue(val, type) {
+ const valueFormat = type === 'week' ? dateTimeFormat.date : val
+ this.$set(this.activeData, 'defaultValue', null)
+ this.$set(this.activeData, 'value-format', valueFormat)
+ this.$set(this.activeData, 'format', val)
+ },
+ spanChange(val) {
+ this.formConf.span = val
+ },
+ multipleChange(val) {
+ this.$set(this.activeData, 'defaultValue', val ? [] : '')
+ },
+ dateTypeChange(val) {
+ this.setTimeValue(dateTimeFormat[val], val)
+ },
+ rangeChange(val) {
+ this.$set(
+ this.activeData,
+ 'defaultValue',
+ val ? [this.activeData.min, this.activeData.max] : this.activeData.min
+ )
+ },
+ rateTextChange(val) {
+ if (val) this.activeData['show-score'] = false
+ },
+ rateScoreChange(val) {
+ if (val) this.activeData['show-text'] = false
+ },
+ colorFormatChange(val) {
+ this.activeData.defaultValue = null
+ this.activeData['show-alpha'] = val.indexOf('a') > -1
+ this.activeData.renderKey = +new Date() // 更新renderKey,重新渲染该组件
+ },
+ openIconsDialog(model) {
+ this.iconsVisible = true
+ this.currentIconModel = model
+ },
+ setIcon(val) {
+ this.activeData[this.currentIconModel] = val
+ },
+ tagChange(tagIcon) {
+ let target = inputComponents.find(item => item.tagIcon === tagIcon)
+ if (!target) target = selectComponents.find(item => item.tagIcon === tagIcon)
+ this.$emit('tag-change', target)
+ }
+ }
+}
+</script>
+
+<style lang="scss" scoped>
+.right-board {
+ width: 350px;
+ position: absolute;
+ right: 0;
+ top: 0;
+ padding-top: 3px;
+ .field-box {
+ position: relative;
+ height: calc(100vh - 42px);
+ box-sizing: border-box;
+ overflow: hidden;
+ }
+ .el-scrollbar {
+ height: 100%;
+ }
+}
+.select-item {
+ display: flex;
+ border: 1px dashed #fff;
+ box-sizing: border-box;
+ & .close-btn {
+ cursor: pointer;
+ color: #f56c6c;
+ }
+ & .el-input + .el-input {
+ margin-left: 4px;
+ }
+}
+.select-item + .select-item {
+ margin-top: 4px;
+}
+.select-item.sortable-chosen {
+ border: 1px dashed #409eff;
+}
+.select-line-icon {
+ line-height: 32px;
+ font-size: 22px;
+ padding: 0 4px;
+ color: #777;
+}
+.option-drag {
+ cursor: move;
+}
+.time-range {
+ .el-date-editor {
+ width: 227px;
+ }
+ ::v-deep .el-icon-time {
+ display: none;
+ }
+}
+.document-link {
+ position: absolute;
+ display: block;
+ width: 26px;
+ height: 26px;
+ top: 0;
+ left: 0;
+ cursor: pointer;
+ background: #409eff;
+ z-index: 1;
+ border-radius: 0 0 6px 0;
+ text-align: center;
+ line-height: 26px;
+ color: #fff;
+ font-size: 18px;
+}
+.node-label{
+ font-size: 14px;
+}
+.node-icon{
+ color: #bebfc3;
+}
+</style>
diff --git a/ruoyi-ui/src/views/tool/build/TreeNodeDialog.vue b/ruoyi-ui/src/views/tool/build/TreeNodeDialog.vue
new file mode 100644
index 0000000..477c010
--- /dev/null
+++ b/ruoyi-ui/src/views/tool/build/TreeNodeDialog.vue
@@ -0,0 +1,149 @@
+<template>
+ <div>
+ <el-dialog
+ v-bind="$attrs"
+ :close-on-click-modal="false"
+ :modal-append-to-body="false"
+ v-on="$listeners"
+ @open="onOpen"
+ @close="onClose"
+ >
+ <el-row :gutter="0">
+ <el-form
+ ref="elForm"
+ :model="formData"
+ :rules="rules"
+ size="small"
+ label-width="100px"
+ >
+ <el-col :span="24">
+ <el-form-item
+ label="选项名"
+ prop="label"
+ >
+ <el-input
+ v-model="formData.label"
+ placeholder="请输入选项名"
+ clearable
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="24">
+ <el-form-item
+ label="选项值"
+ prop="value"
+ >
+ <el-input
+ v-model="formData.value"
+ placeholder="请输入选项值"
+ clearable
+ >
+ <el-select
+ slot="append"
+ v-model="dataType"
+ :style="{width: '100px'}"
+ >
+ <el-option
+ v-for="(item, index) in dataTypeOptions"
+ :key="index"
+ :label="item.label"
+ :value="item.value"
+ :disabled="item.disabled"
+ />
+ </el-select>
+ </el-input>
+ </el-form-item>
+ </el-col>
+ </el-form>
+ </el-row>
+ <div slot="footer">
+ <el-button
+ type="primary"
+ @click="handelConfirm"
+ >
+ 确定
+ </el-button>
+ <el-button @click="close">
+ 取消
+ </el-button>
+ </div>
+ </el-dialog>
+ </div>
+</template>
+<script>
+import { isNumberStr } from '@/utils/index'
+
+export default {
+ components: {},
+ inheritAttrs: false,
+ props: [],
+ data() {
+ return {
+ id: 100,
+ formData: {
+ label: undefined,
+ value: undefined
+ },
+ rules: {
+ label: [
+ {
+ required: true,
+ message: '请输入选项名',
+ trigger: 'blur'
+ }
+ ],
+ value: [
+ {
+ required: true,
+ message: '请输入选项值',
+ trigger: 'blur'
+ }
+ ]
+ },
+ dataType: 'string',
+ dataTypeOptions: [
+ {
+ label: '字符串',
+ value: 'string'
+ },
+ {
+ label: '数字',
+ value: 'number'
+ }
+ ]
+ }
+ },
+ computed: {},
+ watch: {
+ // eslint-disable-next-line func-names
+ 'formData.value': function (val) {
+ this.dataType = isNumberStr(val) ? 'number' : 'string'
+ }
+ },
+ created() {},
+ mounted() {},
+ methods: {
+ onOpen() {
+ this.formData = {
+ label: undefined,
+ value: undefined
+ }
+ },
+ onClose() {},
+ close() {
+ this.$emit('update:visible', false)
+ },
+ handelConfirm() {
+ this.$refs.elForm.validate(valid => {
+ if (!valid) return
+ if (this.dataType === 'number') {
+ this.formData.value = parseFloat(this.formData.value)
+ }
+ this.formData.id = this.id++
+ this.$emit('commit', this.formData)
+ this.close()
+ })
+ }
+ }
+}
+</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..4548012
--- /dev/null
+++ b/ruoyi-ui/src/views/tool/build/index.vue
@@ -0,0 +1,789 @@
+<template>
+ <div class="container">
+ <div class="left-board">
+ <div class="logo-wrapper">
+ <div class="logo">
+ <img :src="logo" alt="logo"> Form Generator
+ </div>
+ </div>
+ <el-scrollbar class="left-scrollbar">
+ <div class="components-list">
+ <div class="components-title">
+ <svg-icon icon-class="component" />输入型组件
+ </div>
+ <draggable
+ class="components-draggable"
+ :list="inputComponents"
+ :group="{ name: 'componentsGroup', pull: 'clone', put: false }"
+ :clone="cloneComponent"
+ draggable=".components-item"
+ :sort="false"
+ @end="onEnd"
+ >
+ <div
+ v-for="(element, index) in inputComponents" :key="index" class="components-item"
+ @click="addComponent(element)"
+ >
+ <div class="components-body">
+ <svg-icon :icon-class="element.tagIcon" />
+ {{ element.label }}
+ </div>
+ </div>
+ </draggable>
+ <div class="components-title">
+ <svg-icon icon-class="component" />选择型组件
+ </div>
+ <draggable
+ class="components-draggable"
+ :list="selectComponents"
+ :group="{ name: 'componentsGroup', pull: 'clone', put: false }"
+ :clone="cloneComponent"
+ draggable=".components-item"
+ :sort="false"
+ @end="onEnd"
+ >
+ <div
+ v-for="(element, index) in selectComponents"
+ :key="index"
+ class="components-item"
+ @click="addComponent(element)"
+ >
+ <div class="components-body">
+ <svg-icon :icon-class="element.tagIcon" />
+ {{ element.label }}
+ </div>
+ </div>
+ </draggable>
+ <div class="components-title">
+ <svg-icon icon-class="component" /> 布局型组件
+ </div>
+ <draggable
+ class="components-draggable" :list="layoutComponents"
+ :group="{ name: 'componentsGroup', pull: 'clone', put: false }" :clone="cloneComponent"
+ draggable=".components-item" :sort="false" @end="onEnd"
+ >
+ <div
+ v-for="(element, index) in layoutComponents" :key="index" class="components-item"
+ @click="addComponent(element)"
+ >
+ <div class="components-body">
+ <svg-icon :icon-class="element.tagIcon" />
+ {{ element.label }}
+ </div>
+ </div>
+ </draggable>
+ </div>
+ </el-scrollbar>
+ </div>
+
+ <div class="center-board">
+ <div class="action-bar">
+ <el-button icon="el-icon-download" type="text" @click="download">
+ 导出vue文件
+ </el-button>
+ <el-button class="copy-btn-main" icon="el-icon-document-copy" type="text" @click="copy">
+ 复制代码
+ </el-button>
+ <el-button class="delete-btn" icon="el-icon-delete" type="text" @click="empty">
+ 清空
+ </el-button>
+ </div>
+ <el-scrollbar class="center-scrollbar">
+ <el-row class="center-board-row" :gutter="formConf.gutter">
+ <el-form
+ :size="formConf.size"
+ :label-position="formConf.labelPosition"
+ :disabled="formConf.disabled"
+ :label-width="formConf.labelWidth + 'px'"
+ >
+ <draggable class="drawing-board" :list="drawingList" :animation="340" group="componentsGroup">
+ <draggable-item
+ v-for="(element, index) in drawingList"
+ :key="element.renderKey"
+ :drawing-list="drawingList"
+ :element="element"
+ :index="index"
+ :active-id="activeId"
+ :form-conf="formConf"
+ @activeItem="activeFormItem"
+ @copyItem="drawingItemCopy"
+ @deleteItem="drawingItemDelete"
+ />
+ </draggable>
+ <div v-show="!drawingList.length" class="empty-info">
+ 从左侧拖入或点选组件进行表单设计
+ </div>
+ </el-form>
+ </el-row>
+ </el-scrollbar>
+ </div>
+
+ <right-panel
+ :active-data="activeData"
+ :form-conf="formConf"
+ :show-field="!!drawingList.length"
+ @tag-change="tagChange"
+ />
+
+ <code-type-dialog
+ :visible.sync="dialogVisible"
+ title="选择生成类型"
+ :show-file-name="showFileName"
+ @confirm="generate"
+ />
+ <input id="copyNode" type="hidden">
+ </div>
+</template>
+
+<script>
+import draggable from 'vuedraggable'
+import { saveAs } from 'file-saver'
+import beautifier from 'js-beautify'
+import ClipboardJS from 'clipboard'
+import render from '@/utils/generator/render'
+import RightPanel from './RightPanel'
+import {
+ inputComponents,
+ selectComponents,
+ layoutComponents,
+ formConf
+} from '@/utils/generator/config'
+import {
+ exportDefault, beautifierConf, isNumberStr, titleCase
+} from '@/utils/index'
+import {
+ makeUpHtml, vueTemplate, vueScript, cssStyle
+} from '@/utils/generator/html'
+import { makeUpJs } from '@/utils/generator/js'
+import { makeUpCss } from '@/utils/generator/css'
+import drawingDefalut from '@/utils/generator/drawingDefalut'
+import logo from '@/assets/logo/logo.png'
+import CodeTypeDialog from './CodeTypeDialog'
+import DraggableItem from './DraggableItem'
+
+const emptyActiveData = { style: {}, autosize: {} }
+let oldActiveId
+let tempActiveData
+
+export default {
+ components: {
+ draggable,
+ render,
+ RightPanel,
+ CodeTypeDialog,
+ DraggableItem
+ },
+ data() {
+ return {
+ logo,
+ idGlobal: 100,
+ formConf,
+ inputComponents,
+ selectComponents,
+ layoutComponents,
+ labelWidth: 100,
+ drawingList: drawingDefalut,
+ drawingData: {},
+ activeId: drawingDefalut[0].formId,
+ drawerVisible: false,
+ formData: {},
+ dialogVisible: false,
+ generateConf: null,
+ showFileName: false,
+ activeData: drawingDefalut[0]
+ }
+ },
+ computed: {
+ },
+ watch: {
+ // eslint-disable-next-line func-names
+ 'activeData.label': function (val, oldVal) {
+ if (
+ this.activeData.placeholder === undefined
+ || !this.activeData.tag
+ || oldActiveId !== this.activeId
+ ) {
+ return
+ }
+ this.activeData.placeholder = this.activeData.placeholder.replace(oldVal, '') + val
+ },
+ activeId: {
+ handler(val) {
+ oldActiveId = val
+ },
+ immediate: true
+ }
+ },
+ mounted() {
+ const clipboard = new ClipboardJS('#copyNode', {
+ text: trigger => {
+ const codeStr = this.generateCode()
+ this.$notify({
+ title: '成功',
+ message: '代码已复制到剪切板,可粘贴。',
+ type: 'success'
+ })
+ return codeStr
+ }
+ })
+ clipboard.on('error', e => {
+ this.$message.error('代码复制失败')
+ })
+ },
+ methods: {
+ activeFormItem(element) {
+ this.activeData = element
+ this.activeId = element.formId
+ },
+ onEnd(obj, a) {
+ if (obj.from !== obj.to) {
+ this.activeData = tempActiveData
+ this.activeId = this.idGlobal
+ }
+ },
+ addComponent(item) {
+ const clone = this.cloneComponent(item)
+ this.drawingList.push(clone)
+ this.activeFormItem(clone)
+ },
+ cloneComponent(origin) {
+ const clone = JSON.parse(JSON.stringify(origin))
+ clone.formId = ++this.idGlobal
+ clone.span = formConf.span
+ clone.renderKey = +new Date() // 改变renderKey后可以实现强制更新组件
+ if (!clone.layout) clone.layout = 'colFormItem'
+ if (clone.layout === 'colFormItem') {
+ clone.vModel = `field${this.idGlobal}`
+ clone.placeholder !== undefined && (clone.placeholder += clone.label)
+ tempActiveData = clone
+ } else if (clone.layout === 'rowFormItem') {
+ delete clone.label
+ clone.componentName = `row${this.idGlobal}`
+ clone.gutter = this.formConf.gutter
+ tempActiveData = clone
+ }
+ return tempActiveData
+ },
+ AssembleFormData() {
+ this.formData = {
+ fields: JSON.parse(JSON.stringify(this.drawingList)),
+ ...this.formConf
+ }
+ },
+ generate(data) {
+ const func = this[`exec${titleCase(this.operationType)}`]
+ this.generateConf = data
+ func && func(data)
+ },
+ execRun(data) {
+ this.AssembleFormData()
+ this.drawerVisible = true
+ },
+ execDownload(data) {
+ const codeStr = this.generateCode()
+ const blob = new Blob([codeStr], { type: 'text/plain;charset=utf-8' })
+ saveAs(blob, data.fileName)
+ },
+ execCopy(data) {
+ document.getElementById('copyNode').click()
+ },
+ empty() {
+ this.$confirm('确定要清空所有组件吗?', '提示', { type: 'warning' }).then(
+ () => {
+ this.drawingList = []
+ }
+ )
+ },
+ drawingItemCopy(item, parent) {
+ let clone = JSON.parse(JSON.stringify(item))
+ clone = this.createIdAndKey(clone)
+ parent.push(clone)
+ this.activeFormItem(clone)
+ },
+ createIdAndKey(item) {
+ item.formId = ++this.idGlobal
+ item.renderKey = +new Date()
+ if (item.layout === 'colFormItem') {
+ item.vModel = `field${this.idGlobal}`
+ } else if (item.layout === 'rowFormItem') {
+ item.componentName = `row${this.idGlobal}`
+ }
+ if (Array.isArray(item.children)) {
+ item.children = item.children.map(childItem => this.createIdAndKey(childItem))
+ }
+ return item
+ },
+ drawingItemDelete(index, parent) {
+ parent.splice(index, 1)
+ this.$nextTick(() => {
+ const len = this.drawingList.length
+ if (len) {
+ this.activeFormItem(this.drawingList[len - 1])
+ }
+ })
+ },
+ generateCode() {
+ const { type } = this.generateConf
+ this.AssembleFormData()
+ const script = vueScript(makeUpJs(this.formData, type))
+ const html = vueTemplate(makeUpHtml(this.formData, type))
+ const css = cssStyle(makeUpCss(this.formData))
+ return beautifier.html(html + script + css, beautifierConf.html)
+ },
+ download() {
+ this.dialogVisible = true
+ this.showFileName = true
+ this.operationType = 'download'
+ },
+ run() {
+ this.dialogVisible = true
+ this.showFileName = false
+ this.operationType = 'run'
+ },
+ copy() {
+ this.dialogVisible = true
+ this.showFileName = false
+ this.operationType = 'copy'
+ },
+ tagChange(newTag) {
+ newTag = this.cloneComponent(newTag)
+ newTag.vModel = this.activeData.vModel
+ newTag.formId = this.activeId
+ newTag.span = this.activeData.span
+ delete this.activeData.tag
+ delete this.activeData.tagIcon
+ delete this.activeData.document
+ Object.keys(newTag).forEach(key => {
+ if (this.activeData[key] !== undefined
+ && typeof this.activeData[key] === typeof newTag[key]) {
+ newTag[key] = this.activeData[key]
+ }
+ })
+ this.activeData = newTag
+ this.updateDrawingList(newTag, this.drawingList)
+ },
+ updateDrawingList(newTag, list) {
+ const index = list.findIndex(item => item.formId === this.activeId)
+ if (index > -1) {
+ list.splice(index, 1, newTag)
+ } else {
+ list.forEach(item => {
+ if (Array.isArray(item.children)) this.updateDrawingList(newTag, item.children)
+ })
+ }
+ }
+ }
+}
+</script>
+
+<style lang='scss'>
+body, html{
+ margin: 0;
+ padding: 0;
+ background: #fff;
+ -moz-osx-font-smoothing: grayscale;
+ -webkit-font-smoothing: antialiased;
+ text-rendering: optimizeLegibility;
+ font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji;
+}
+
+input, textarea{
+ font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji;
+}
+
+.editor-tabs{
+ background: #121315;
+ .el-tabs__header{
+ margin: 0;
+ border-bottom-color: #121315;
+ .el-tabs__nav{
+ border-color: #121315;
+ }
+ }
+ .el-tabs__item{
+ height: 32px;
+ line-height: 32px;
+ color: #888a8e;
+ border-left: 1px solid #121315 !important;
+ background: #363636;
+ margin-right: 5px;
+ user-select: none;
+ }
+ .el-tabs__item.is-active{
+ background: #1e1e1e;
+ border-bottom-color: #1e1e1e!important;
+ color: #fff;
+ }
+ .el-icon-edit{
+ color: #f1fa8c;
+ }
+ .el-icon-document{
+ color: #a95812;
+ }
+}
+
+// home
+.right-scrollbar {
+ .el-scrollbar__view {
+ padding: 12px 18px 15px 15px;
+ }
+}
+.left-scrollbar .el-scrollbar__wrap {
+ box-sizing: border-box;
+ overflow-x: hidden !important;
+ margin-bottom: 0 !important;
+}
+.center-tabs{
+ .el-tabs__header{
+ margin-bottom: 0!important;
+ }
+ .el-tabs__item{
+ width: 50%;
+ text-align: center;
+ }
+ .el-tabs__nav{
+ width: 100%;
+ }
+}
+.reg-item{
+ padding: 12px 6px;
+ background: #f8f8f8;
+ position: relative;
+ border-radius: 4px;
+ .close-btn{
+ position: absolute;
+ right: -6px;
+ top: -6px;
+ display: block;
+ width: 16px;
+ height: 16px;
+ line-height: 16px;
+ background: rgba(0, 0, 0, 0.2);
+ border-radius: 50%;
+ color: #fff;
+ text-align: center;
+ z-index: 1;
+ cursor: pointer;
+ font-size: 12px;
+ &:hover{
+ background: rgba(210, 23, 23, 0.5)
+ }
+ }
+ & + .reg-item{
+ margin-top: 18px;
+ }
+}
+.action-bar{
+ & .el-button+.el-button {
+ margin-left: 15px;
+ }
+ & i {
+ font-size: 20px;
+ vertical-align: middle;
+ position: relative;
+ top: -1px;
+ }
+}
+
+.custom-tree-node{
+ width: 100%;
+ font-size: 14px;
+ .node-operation{
+ float: right;
+ }
+ i[class*="el-icon"] + i[class*="el-icon"]{
+ margin-left: 6px;
+ }
+ .el-icon-plus{
+ color: #409EFF;
+ }
+ .el-icon-delete{
+ color: #157a0c;
+ }
+}
+
+.left-scrollbar .el-scrollbar__view{
+ overflow-x: hidden;
+}
+
+.el-rate{
+ display: inline-block;
+ vertical-align: text-top;
+}
+.el-upload__tip{
+ line-height: 1.2;
+}
+
+$selectedColor: #f6f7ff;
+$lighterBlue: #409EFF;
+
+.container {
+ position: relative;
+ width: 100%;
+ height: 100%;
+}
+
+.components-list {
+ padding: 8px;
+ box-sizing: border-box;
+ height: 100%;
+ .components-item {
+ display: inline-block;
+ width: 48%;
+ margin: 1%;
+ transition: transform 0ms !important;
+ }
+}
+.components-draggable{
+ padding-bottom: 20px;
+}
+.components-title{
+ font-size: 14px;
+ color: #222;
+ margin: 6px 2px;
+ .svg-icon{
+ color: #666;
+ font-size: 18px;
+ }
+}
+
+.components-body {
+ padding: 8px 10px;
+ background: $selectedColor;
+ font-size: 12px;
+ cursor: move;
+ border: 1px dashed $selectedColor;
+ border-radius: 3px;
+ .svg-icon{
+ color: #777;
+ font-size: 15px;
+ }
+ &:hover {
+ border: 1px dashed #787be8;
+ color: #787be8;
+ .svg-icon {
+ color: #787be8;
+ }
+ }
+}
+
+.left-board {
+ width: 260px;
+ position: absolute;
+ left: 0;
+ top: 0;
+ height: 100vh;
+}
+.left-scrollbar{
+ height: calc(100vh - 42px);
+ overflow: hidden;
+}
+.center-scrollbar {
+ height: calc(100vh - 42px);
+ overflow: hidden;
+ border-left: 1px solid #f1e8e8;
+ border-right: 1px solid #f1e8e8;
+ box-sizing: border-box;
+}
+.center-board {
+ height: 100vh;
+ width: auto;
+ margin: 0 350px 0 260px;
+ box-sizing: border-box;
+}
+.empty-info{
+ position: absolute;
+ top: 46%;
+ left: 0;
+ right: 0;
+ text-align: center;
+ font-size: 18px;
+ color: #ccb1ea;
+ letter-spacing: 4px;
+}
+.action-bar{
+ position: relative;
+ height: 42px;
+ text-align: right;
+ padding: 0 15px;
+ box-sizing: border-box;;
+ border: 1px solid #f1e8e8;
+ border-top: none;
+ border-left: none;
+ .delete-btn{
+ color: #F56C6C;
+ }
+}
+.logo-wrapper{
+ position: relative;
+ height: 42px;
+ background: #fff;
+ border-bottom: 1px solid #f1e8e8;
+ box-sizing: border-box;
+}
+.logo{
+ position: absolute;
+ left: 12px;
+ top: 6px;
+ line-height: 30px;
+ color: #00afff;
+ font-weight: 600;
+ font-size: 17px;
+ white-space: nowrap;
+ > img{
+ width: 30px;
+ height: 30px;
+ vertical-align: top;
+ }
+ .github{
+ display: inline-block;
+ vertical-align: sub;
+ margin-left: 15px;
+ > img{
+ height: 22px;
+ }
+ }
+}
+
+.center-board-row {
+ padding: 12px 12px 15px 12px;
+ box-sizing: border-box;
+ & > .el-form {
+ // 69 = 12+15+42
+ height: calc(100vh - 69px);
+ }
+}
+.drawing-board {
+ height: 100%;
+ position: relative;
+ .components-body {
+ padding: 0;
+ margin: 0;
+ font-size: 0;
+ }
+ .sortable-ghost {
+ position: relative;
+ display: block;
+ overflow: hidden;
+ &::before {
+ content: " ";
+ position: absolute;
+ left: 0;
+ right: 0;
+ top: 0;
+ height: 3px;
+ background: rgb(89, 89, 223);
+ z-index: 2;
+ }
+ }
+ .components-item.sortable-ghost {
+ width: 100%;
+ height: 60px;
+ background-color: $selectedColor;
+ }
+ .active-from-item {
+ & > .el-form-item{
+ background: $selectedColor;
+ border-radius: 6px;
+ }
+ & > .drawing-item-copy, & > .drawing-item-delete{
+ display: initial;
+ }
+ & > .component-name{
+ color: $lighterBlue;
+ }
+ }
+ .el-form-item{
+ margin-bottom: 15px;
+ }
+}
+.drawing-item{
+ position: relative;
+ cursor: move;
+ &.unfocus-bordered:not(.activeFromItem) > div:first-child {
+ border: 1px dashed #ccc;
+ }
+ .el-form-item{
+ padding: 12px 10px;
+ }
+}
+.drawing-row-item{
+ position: relative;
+ cursor: move;
+ box-sizing: border-box;
+ border: 1px dashed #ccc;
+ border-radius: 3px;
+ padding: 0 2px;
+ margin-bottom: 15px;
+ .drawing-row-item {
+ margin-bottom: 2px;
+ }
+ .el-col{
+ margin-top: 22px;
+ }
+ .el-form-item{
+ margin-bottom: 0;
+ }
+ .drag-wrapper{
+ min-height: 80px;
+ }
+ &.active-from-item{
+ border: 1px dashed $lighterBlue;
+ }
+ .component-name{
+ position: absolute;
+ top: 0;
+ left: 0;
+ font-size: 12px;
+ color: #bbb;
+ display: inline-block;
+ padding: 0 6px;
+ }
+}
+.drawing-item, .drawing-row-item{
+ &:hover {
+ & > .el-form-item{
+ background: $selectedColor;
+ border-radius: 6px;
+ }
+ & > .drawing-item-copy, & > .drawing-item-delete{
+ display: initial;
+ }
+ }
+ & > .drawing-item-copy, & > .drawing-item-delete{
+ display: none;
+ position: absolute;
+ top: -10px;
+ width: 22px;
+ height: 22px;
+ line-height: 22px;
+ text-align: center;
+ border-radius: 50%;
+ font-size: 12px;
+ border: 1px solid;
+ cursor: pointer;
+ z-index: 1;
+ }
+ & > .drawing-item-copy{
+ right: 56px;
+ border-color: $lighterBlue;
+ color: $lighterBlue;
+ background: #fff;
+ &:hover{
+ background: $lighterBlue;
+ color: #fff;
+ }
+ }
+ & > .drawing-item-delete{
+ right: 24px;
+ border-color: #F56C6C;
+ color: #F56C6C;
+ background: #fff;
+ &:hover{
+ background: #F56C6C;
+ color: #fff;
+ }
+ }
+}
+
+</style>
diff --git a/ruoyi-ui/src/views/tool/gen/basicInfoForm.vue b/ruoyi-ui/src/views/tool/gen/basicInfoForm.vue
new file mode 100644
index 0000000..f3e8717
--- /dev/null
+++ b/ruoyi-ui/src/views/tool/gen/basicInfoForm.vue
@@ -0,0 +1,61 @@
+<template>
+ <el-form ref="basicInfoForm" :model="info" :rules="rules" label-width="150px">
+ <el-row>
+ <el-col :span="12">
+ <el-form-item label="表名称" prop="tableName">
+ <el-input placeholder="请输入仓库名称" v-model="info.tableName" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="表描述" prop="tableComment">
+ <el-input placeholder="请输入" v-model="info.tableComment" />
+ </el-form-item>
+ </el-col>
+
+ <el-col :span="12">
+ <el-form-item label="实体类名称" prop="className">
+ <el-input placeholder="请输入" v-model="info.className" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="作者" prop="functionAuthor">
+ <el-input placeholder="请输入" v-model="info.functionAuthor" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="24">
+ <el-form-item label="备注" prop="remark">
+ <el-input type="textarea" :rows="3" v-model="info.remark"></el-input>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ </el-form>
+</template>
+<script>
+export default {
+ name: "BasicInfoForm",
+ props: {
+ info: {
+ type: Object,
+ default: null
+ }
+ },
+ data() {
+ return {
+ rules: {
+ tableName: [
+ { required: true, message: "请输入表名称", trigger: "blur" }
+ ],
+ tableComment: [
+ { required: true, message: "请输入表描述", trigger: "blur" }
+ ],
+ className: [
+ { required: true, message: "请输入实体类名称", trigger: "blur" }
+ ],
+ functionAuthor: [
+ { required: true, message: "请输入作者", trigger: "blur" }
+ ]
+ }
+ };
+ }
+};
+</script>
diff --git a/ruoyi-ui/src/views/tool/gen/editTable.vue b/ruoyi-ui/src/views/tool/gen/editTable.vue
new file mode 100644
index 0000000..5f88c1a
--- /dev/null
+++ b/ruoyi-ui/src/views/tool/gen/editTable.vue
@@ -0,0 +1,217 @@
+<template>
+ <el-card>
+ <el-tabs v-model="activeName">
+ <el-tab-pane label="基本信息" name="basic">
+ <basic-info-form ref="basicInfo" :info="info" />
+ </el-tab-pane>
+ <el-tab-pane label="字段信息" name="cloum">
+ <el-table ref="dragTable" :data="cloumns" row-key="columnId" :max-height="tableHeight">
+ <el-table-column label="序号" type="index" min-width="5%" class-name="allowDrag" />
+ <el-table-column
+ label="字段列名"
+ prop="columnName"
+ min-width="10%"
+ :show-overflow-tooltip="true"
+ />
+ <el-table-column label="字段描述" min-width="10%">
+ <template slot-scope="scope">
+ <el-input v-model="scope.row.columnComment"></el-input>
+ </template>
+ </el-table-column>
+ <el-table-column
+ label="物理类型"
+ prop="columnType"
+ min-width="10%"
+ :show-overflow-tooltip="true"
+ />
+ <el-table-column label="Java类型" min-width="11%">
+ <template slot-scope="scope">
+ <el-select v-model="scope.row.javaType">
+ <el-option label="Long" value="Long" />
+ <el-option label="String" value="String" />
+ <el-option label="Integer" value="Integer" />
+ <el-option label="Double" value="Double" />
+ <el-option label="BigDecimal" value="BigDecimal" />
+ <el-option label="Date" value="Date" />
+ </el-select>
+ </template>
+ </el-table-column>
+ <el-table-column label="java属性" min-width="10%">
+ <template slot-scope="scope">
+ <el-input v-model="scope.row.javaField"></el-input>
+ </template>
+ </el-table-column>
+
+ <el-table-column label="插入" min-width="5%">
+ <template slot-scope="scope">
+ <el-checkbox true-label="1" v-model="scope.row.isInsert"></el-checkbox>
+ </template>
+ </el-table-column>
+ <el-table-column label="编辑" min-width="5%">
+ <template slot-scope="scope">
+ <el-checkbox true-label="1" v-model="scope.row.isEdit"></el-checkbox>
+ </template>
+ </el-table-column>
+ <el-table-column label="列表" min-width="5%">
+ <template slot-scope="scope">
+ <el-checkbox true-label="1" v-model="scope.row.isList"></el-checkbox>
+ </template>
+ </el-table-column>
+ <el-table-column label="查询" min-width="5%">
+ <template slot-scope="scope">
+ <el-checkbox true-label="1" v-model="scope.row.isQuery"></el-checkbox>
+ </template>
+ </el-table-column>
+ <el-table-column label="查询方式" min-width="10%">
+ <template slot-scope="scope">
+ <el-select v-model="scope.row.queryType">
+ <el-option label="=" value="EQ" />
+ <el-option label="!=" value="NE" />
+ <el-option label=">" value="GT" />
+ <el-option label=">=" value="GTE" />
+ <el-option label="<" value="LT" />
+ <el-option label="<=" value="LTE" />
+ <el-option label="LIKE" value="LIKE" />
+ <el-option label="BETWEEN" value="BETWEEN" />
+ </el-select>
+ </template>
+ </el-table-column>
+ <el-table-column label="必填" min-width="5%">
+ <template slot-scope="scope">
+ <el-checkbox true-label="1" v-model="scope.row.isRequired"></el-checkbox>
+ </template>
+ </el-table-column>
+ <el-table-column label="显示类型" min-width="12%">
+ <template slot-scope="scope">
+ <el-select v-model="scope.row.htmlType">
+ <el-option label="文本框" value="input" />
+ <el-option label="文本域" value="textarea" />
+ <el-option label="下拉框" value="select" />
+ <el-option label="单选框" value="radio" />
+ <el-option label="复选框" value="checkbox" />
+ <el-option label="日期控件" value="datetime" />
+ </el-select>
+ </template>
+ </el-table-column>
+ <el-table-column label="字典类型" min-width="12%">
+ <template slot-scope="scope">
+ <el-select v-model="scope.row.dictType" clearable filterable placeholder="请选择">
+ <el-option
+ v-for="dict in dictOptions"
+ :key="dict.dictType"
+ :label="dict.dictName"
+ :value="dict.dictType">
+ <span style="float: left">{{ dict.dictName }}</span>
+ <span style="float: right; color: #8492a6; font-size: 13px">{{ dict.dictType }}</span>
+ </el-option>
+ </el-select>
+ </template>
+ </el-table-column>
+ </el-table>
+ </el-tab-pane>
+ <el-tab-pane label="生成信息" name="genInfo">
+ <gen-info-form ref="genInfo" :info="info" />
+ </el-tab-pane>
+ </el-tabs>
+ <el-form label-width="100px">
+ <el-form-item style="text-align: center;margin-left:-100px;margin-top:10px;">
+ <el-button type="primary" @click="submitForm()">提交</el-button>
+ <el-button @click="close()">返回</el-button>
+ </el-form-item>
+ </el-form>
+ </el-card>
+</template>
+<script>
+import { getGenTable, updateGenTable } from "@/api/tool/gen";
+import { optionselect as getDictOptionselect } from "@/api/system/dict/type";
+import basicInfoForm from "./basicInfoForm";
+import genInfoForm from "./genInfoForm";
+import Sortable from 'sortablejs'
+export default {
+ name: "GenEdit",
+ components: {
+ basicInfoForm,
+ genInfoForm
+ },
+ data() {
+ return {
+ // 选中选项卡的 name
+ activeName: "cloum",
+ // 表格的高度
+ tableHeight: document.documentElement.scrollHeight - 245 + "px",
+ // 表列信息
+ cloumns: [],
+ // 字典信息
+ dictOptions: [],
+ // 表详细信息
+ info: {}
+ };
+ },
+ beforeCreate() {
+ const { tableId } = this.$route.query;
+ if (tableId) {
+ // 获取表详细信息
+ getGenTable(tableId).then(res => {
+ this.cloumns = res.data.rows;
+ this.info = res.data.info;
+ });
+ /** 查询字典下拉列表 */
+ getDictOptionselect().then(response => {
+ this.dictOptions = response.data;
+ });
+ }
+ },
+ methods: {
+ /** 提交按钮 */
+ submitForm() {
+ const basicForm = this.$refs.basicInfo.$refs.basicInfoForm;
+ const genForm = this.$refs.genInfo.$refs.genInfoForm;
+ Promise.all([basicForm, genForm].map(this.getFormPromise)).then(res => {
+ const validateResult = res.every(item => !!item);
+ if (validateResult) {
+ const genTable = Object.assign({}, basicForm.model, genForm.model);
+ genTable.columns = this.cloumns;
+ genTable.params = {
+ treeCode: genTable.treeCode,
+ treeName: genTable.treeName,
+ treeParentCode: genTable.treeParentCode
+ };
+ updateGenTable(genTable).then(res => {
+ this.msgSuccess(res.msg);
+ if (res.code === 200) {
+ this.close();
+ }
+ });
+ } else {
+ this.msgError("表单校验未通过,请重新检查提交内容");
+ }
+ });
+ },
+ getFormPromise(form) {
+ return new Promise(resolve => {
+ form.validate(res => {
+ resolve(res);
+ });
+ });
+ },
+ /** 关闭按钮 */
+ close() {
+ this.$store.dispatch("tagsView/delView", this.$route);
+ this.$router.push({ path: "/tool/gen", query: { t: Date.now()}})
+ }
+ },
+ mounted() {
+ const el = this.$refs.dragTable.$el.querySelectorAll(".el-table__body-wrapper > table > tbody")[0];
+ const sortable = Sortable.create(el, {
+ handle: ".allowDrag",
+ onEnd: evt => {
+ const targetRow = this.cloumns.splice(evt.oldIndex, 1)[0];
+ this.cloumns.splice(evt.newIndex, 0, targetRow);
+ for (let index in this.cloumns) {
+ this.cloumns[index].sort = parseInt(index) + 1;
+ }
+ }
+ });
+ }
+};
+</script>
diff --git a/ruoyi-ui/src/views/tool/gen/genInfoForm.vue b/ruoyi-ui/src/views/tool/gen/genInfoForm.vue
new file mode 100644
index 0000000..41ad4e5
--- /dev/null
+++ b/ruoyi-ui/src/views/tool/gen/genInfoForm.vue
@@ -0,0 +1,154 @@
+<template>
+ <el-form ref="genInfoForm" :model="info" :rules="rules" label-width="150px">
+ <el-row>
+ <el-col :span="12">
+ <el-form-item prop="tplCategory">
+ <span slot="label">生成模板</span>
+ <el-select v-model="info.tplCategory">
+ <el-option label="单表(增删改查)" value="crud" />
+ <el-option label="树表(增删改查)" value="tree"/>
+ </el-select>
+ </el-form-item>
+ </el-col>
+
+ <el-col :span="12">
+ <el-form-item prop="packageName">
+ <span slot="label">
+ 生成包路径
+ <el-tooltip content="生成在哪个java包下,例如 com.ruoyi.system" placement="top">
+ <i class="el-icon-question"></i>
+ </el-tooltip>
+ </span>
+ <el-input v-model="info.packageName" />
+ </el-form-item>
+ </el-col>
+
+ <el-col :span="12">
+ <el-form-item prop="moduleName">
+ <span slot="label">
+ 生成模块名
+ <el-tooltip content="可理解为子系统名,例如 system" placement="top">
+ <i class="el-icon-question"></i>
+ </el-tooltip>
+ </span>
+ <el-input v-model="info.moduleName" />
+ </el-form-item>
+ </el-col>
+
+ <el-col :span="12">
+ <el-form-item prop="businessName">
+ <span slot="label">
+ 生成业务名
+ <el-tooltip content="可理解为功能英文名,例如 user" placement="top">
+ <i class="el-icon-question"></i>
+ </el-tooltip>
+ </span>
+ <el-input v-model="info.businessName" />
+ </el-form-item>
+ </el-col>
+
+ <el-col :span="12">
+ <el-form-item prop="functionName">
+ <span slot="label">
+ 生成功能名
+ <el-tooltip content="用作类描述,例如 用户" placement="top">
+ <i class="el-icon-question"></i>
+ </el-tooltip>
+ </span>
+ <el-input v-model="info.functionName" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+
+ <el-row v-show="info.tplCategory == 'tree'">
+ <h4 class="form-header">其他信息</h4>
+ <el-col :span="12">
+ <el-form-item>
+ <span slot="label">
+ 树编码字段
+ <el-tooltip content="树显示的编码字段名, 如:dept_id" placement="top">
+ <i class="el-icon-question"></i>
+ </el-tooltip>
+ </span>
+ <el-select v-model="info.treeCode" placeholder="请选择">
+ <el-option
+ v-for="column in info.columns"
+ :key="column.columnName"
+ :label="column.columnName + ':' + column.columnComment"
+ :value="column.columnName"
+ ></el-option>
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item>
+ <span slot="label">
+ 树父编码字段
+ <el-tooltip content="树显示的父编码字段名, 如:parent_Id" placement="top">
+ <i class="el-icon-question"></i>
+ </el-tooltip>
+ </span>
+ <el-select v-model="info.treeParentCode" placeholder="请选择">
+ <el-option
+ v-for="column in info.columns"
+ :key="column.columnName"
+ :label="column.columnName + ':' + column.columnComment"
+ :value="column.columnName"
+ ></el-option>
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item>
+ <span slot="label">
+ 树名称字段
+ <el-tooltip content="树节点的显示名称字段名, 如:dept_name" placement="top">
+ <i class="el-icon-question"></i>
+ </el-tooltip>
+ </span>
+ <el-select v-model="info.treeName" placeholder="请选择">
+ <el-option
+ v-for="column in info.columns"
+ :key="column.columnName"
+ :label="column.columnName + ':' + column.columnComment"
+ :value="column.columnName"
+ ></el-option>
+ </el-select>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ </el-form>
+</template>
+<script>
+export default {
+ name: "BasicInfoForm",
+ props: {
+ info: {
+ type: Object,
+ default: null
+ }
+ },
+ data() {
+ return {
+ rules: {
+ tplCategory: [
+ { required: true, message: "请选择生成模板", trigger: "blur" }
+ ],
+ packageName: [
+ { required: true, message: "请输入生成包路径", trigger: "blur" }
+ ],
+ moduleName: [
+ { required: true, message: "请输入生成模块名", trigger: "blur" }
+ ],
+ businessName: [
+ { required: true, message: "请输入生成业务名", trigger: "blur" }
+ ],
+ functionName: [
+ { required: true, message: "请输入生成功能名", trigger: "blur" }
+ ]
+ }
+ };
+ },
+ created() {}
+};
+</script>
diff --git a/ruoyi-ui/src/views/tool/gen/importTable.vue b/ruoyi-ui/src/views/tool/gen/importTable.vue
new file mode 100644
index 0000000..6c56c77
--- /dev/null
+++ b/ruoyi-ui/src/views/tool/gen/importTable.vue
@@ -0,0 +1,117 @@
+<template>
+ <!-- 导入表 -->
+ <el-dialog title="导入表" :visible.sync="visible" width="800px" top="5vh" append-to-body>
+ <el-form :model="queryParams" ref="queryForm" :inline="true">
+ <el-form-item label="表名称" prop="tableName">
+ <el-input
+ v-model="queryParams.tableName"
+ placeholder="请输入表名称"
+ clearable
+ size="small"
+ @keyup.enter.native="handleQuery"
+ />
+ </el-form-item>
+ <el-form-item label="表描述" prop="tableComment">
+ <el-input
+ v-model="queryParams.tableComment"
+ placeholder="请输入表描述"
+ clearable
+ size="small"
+ @keyup.enter.native="handleQuery"
+ />
+ </el-form-item>
+ <el-form-item>
+ <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+ <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+ </el-form-item>
+ </el-form>
+ <el-row>
+ <el-table @row-click="clickRow" ref="table" :data="dbTableList" @selection-change="handleSelectionChange" height="260px">
+ <el-table-column type="selection" width="55"></el-table-column>
+ <el-table-column prop="tableName" label="表名称"></el-table-column>
+ <el-table-column prop="tableComment" label="表描述"></el-table-column>
+ <el-table-column prop="createTime" label="创建时间"></el-table-column>
+ <el-table-column prop="updateTime" label="更新时间"></el-table-column>
+ </el-table>
+ <pagination
+ v-show="total>0"
+ :total="total"
+ :page.sync="queryParams.pageNum"
+ :limit.sync="queryParams.pageSize"
+ @pagination="getList"
+ />
+ </el-row>
+ <div slot="footer" class="dialog-footer">
+ <el-button type="primary" @click="handleImportTable">确 定</el-button>
+ <el-button @click="visible = false">取 消</el-button>
+ </div>
+ </el-dialog>
+</template>
+
+<script>
+import { listDbTable, importTable } from "@/api/tool/gen";
+export default {
+ data() {
+ return {
+ // 遮罩层
+ visible: false,
+ // 选中数组值
+ tables: [],
+ // 总条数
+ total: 0,
+ // 表数据
+ dbTableList: [],
+ // 查询参数
+ queryParams: {
+ pageNum: 1,
+ pageSize: 10,
+ tableName: undefined,
+ tableComment: undefined
+ }
+ };
+ },
+ methods: {
+ // 显示弹框
+ show() {
+ this.getList();
+ this.visible = true;
+ },
+ clickRow(row) {
+ this.$refs.table.toggleRowSelection(row);
+ },
+ // 多选框选中数据
+ handleSelectionChange(selection) {
+ this.tables = selection.map(item => item.tableName);
+ },
+ // 查询表数据
+ getList() {
+ listDbTable(this.queryParams).then(res => {
+ if (res.code === 200) {
+ this.dbTableList = res.rows;
+ this.total = res.total;
+ }
+ });
+ },
+ /** 搜索按钮操作 */
+ handleQuery() {
+ this.queryParams.pageNum = 1;
+ this.getList();
+ },
+ /** 重置按钮操作 */
+ resetQuery() {
+ this.resetForm("queryForm");
+ this.handleQuery();
+ },
+ /** 导入按钮操作 */
+ handleImportTable() {
+ importTable({ tables: this.tables.join(",") }).then(res => {
+ this.msgSuccess(res.msg);
+ if (res.code === 200) {
+ this.visible = false;
+ this.$emit("ok");
+ }
+ });
+ }
+ }
+};
+</script>
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..3d71204
--- /dev/null
+++ b/ruoyi-ui/src/views/tool/gen/index.vue
@@ -0,0 +1,291 @@
+<template>
+ <div class="app-container">
+ <el-form :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
+ <el-form-item label="表名称" prop="tableName">
+ <el-input
+ v-model="queryParams.tableName"
+ placeholder="请输入表名称"
+ clearable
+ size="small"
+ @keyup.enter.native="handleQuery"
+ />
+ </el-form-item>
+ <el-form-item label="表描述" prop="tableComment">
+ <el-input
+ v-model="queryParams.tableComment"
+ placeholder="请输入表描述"
+ clearable
+ size="small"
+ @keyup.enter.native="handleQuery"
+ />
+ </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 icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+ </el-form-item>
+ </el-form>
+
+ <el-row :gutter="10" class="mb8">
+ <el-col :span="1.5">
+ <el-button
+ type="primary"
+ icon="el-icon-download"
+ size="mini"
+ @click="handleGenTable"
+ v-hasPermi="['tool:gen:code']"
+ >生成</el-button>
+ </el-col>
+ <el-col :span="1.5">
+ <el-button
+ type="info"
+ icon="el-icon-upload"
+ size="mini"
+ @click="openImportTable"
+ v-hasPermi="['tool:gen:import']"
+ >导入</el-button>
+ </el-col>
+ <el-col :span="1.5">
+ <el-button
+ type="success"
+ icon="el-icon-edit"
+ size="mini"
+ :disabled="single"
+ @click="handleEditTable"
+ v-hasPermi="['tool:gen:edit']"
+ >修改</el-button>
+ </el-col>
+ <el-col :span="1.5">
+ <el-button
+ type="danger"
+ icon="el-icon-delete"
+ size="mini"
+ :disabled="multiple"
+ @click="handleDelete"
+ v-hasPermi="['tool:gen:remove']"
+ >删除</el-button>
+ </el-col>
+ </el-row>
+
+ <el-table v-loading="loading" :data="tableList" @selection-change="handleSelectionChange">
+ <el-table-column type="selection" width="55"></el-table-column>
+ <el-table-column label="序号" type="index" width="50" align="center">
+ <template slot-scope="scope">
+ <span>{{(queryParams.pageNum - 1) * queryParams.pageSize + scope.$index + 1}}</span>
+ </template>
+ </el-table-column>
+ <el-table-column
+ label="表名称"
+ align="center"
+ prop="tableName"
+ :show-overflow-tooltip="true"
+ width="130"
+ />
+ <el-table-column
+ label="表描述"
+ align="center"
+ prop="tableComment"
+ :show-overflow-tooltip="true"
+ width="130"
+ />
+ <el-table-column
+ label="实体"
+ align="center"
+ prop="className"
+ :show-overflow-tooltip="true"
+ width="130"
+ />
+ <el-table-column label="创建时间" align="center" prop="createTime" width="160" />
+ <el-table-column label="更新时间" align="center" prop="updateTime" width="160" />
+ <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+ <template slot-scope="scope">
+ <el-button
+ type="text"
+ size="small"
+ icon="el-icon-view"
+ @click="handlePreview(scope.row)"
+ v-hasPermi="['tool:gen:preview']"
+ >预览</el-button>
+ <el-button
+ type="text"
+ size="small"
+ icon="el-icon-edit"
+ @click="handleEditTable(scope.row)"
+ v-hasPermi="['tool:gen:edit']"
+ >编辑</el-button>
+ <el-button
+ type="text"
+ size="small"
+ icon="el-icon-delete"
+ @click="handleDelete(scope.row)"
+ v-hasPermi="['tool:gen:remove']"
+ >删除</el-button>
+ <el-button
+ type="text"
+ size="small"
+ icon="el-icon-download"
+ @click="handleGenTable(scope.row)"
+ v-hasPermi="['tool:gen:code']"
+ >生成代码</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="preview.title" :visible.sync="preview.open" width="80%" top="5vh" append-to-body>
+ <el-tabs v-model="preview.activeName">
+ <el-tab-pane
+ v-for="(value, key) in preview.data"
+ :label="key.substring(key.lastIndexOf('/')+1,key.indexOf('.vm'))"
+ :name="key.substring(key.lastIndexOf('/')+1,key.indexOf('.vm'))"
+ :key="key"
+ >
+ <pre>{{ value }}</pre>
+ </el-tab-pane>
+ </el-tabs>
+ </el-dialog>
+ <import-table ref="import" @ok="handleQuery" />
+ </div>
+</template>
+
+<script>
+import { listTable, previewTable, delTable } from "@/api/tool/gen";
+import importTable from "./importTable";
+import { downLoadZip } from "@/utils/zipdownload";
+export default {
+ name: "Gen",
+ components: { importTable },
+ data() {
+ return {
+ // 遮罩层
+ loading: true,
+ // 唯一标识符
+ uniqueId: "",
+ // 选中数组
+ ids: [],
+ // 选中表数组
+ tableNames: [],
+ // 非单个禁用
+ single: true,
+ // 非多个禁用
+ multiple: true,
+ // 总条数
+ total: 0,
+ // 表数据
+ tableList: [],
+ // 日期范围
+ dateRange: "",
+ // 查询参数
+ queryParams: {
+ pageNum: 1,
+ pageSize: 10,
+ tableName: undefined,
+ tableComment: undefined
+ },
+ // 预览参数
+ preview: {
+ open: false,
+ title: "代码预览",
+ data: {},
+ activeName: "domain.java"
+ }
+ };
+ },
+ created() {
+ this.getList();
+ },
+ activated() {
+ const time = this.$route.query.t;
+ if (time != null && time != this.uniqueId) {
+ this.uniqueId = time;
+ this.resetQuery();
+ }
+ },
+ methods: {
+ /** 查询表集合 */
+ getList() {
+ this.loading = true;
+ listTable(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
+ this.tableList = response.rows;
+ this.total = response.total;
+ this.loading = false;
+ }
+ );
+ },
+ /** 搜索按钮操作 */
+ handleQuery() {
+ this.queryParams.pageNum = 1;
+ this.getList();
+ },
+ /** 生成代码操作 */
+ handleGenTable(row) {
+ const tableNames = row.tableName || this.tableNames;
+ if (tableNames == "") {
+ this.msgError("请选择要生成的数据");
+ return;
+ }
+ downLoadZip("code/gen/batchGenCode?tables=" + tableNames, "ruoyi");
+ },
+ /** 打开导入表弹窗 */
+ openImportTable() {
+ this.$refs.import.show();
+ },
+ /** 重置按钮操作 */
+ resetQuery() {
+ this.dateRange = [];
+ this.resetForm("queryForm");
+ this.handleQuery();
+ },
+ /** 预览按钮 */
+ handlePreview(row) {
+ previewTable(row.tableId).then(response => {
+ this.preview.data = response.data;
+ this.preview.open = true;
+ });
+ },
+ // 多选框选中数据
+ handleSelectionChange(selection) {
+ this.ids = selection.map(item => item.tableId);
+ this.tableNames = selection.map(item => item.tableName);
+ this.single = selection.length != 1;
+ this.multiple = !selection.length;
+ },
+ /** 修改按钮操作 */
+ handleEditTable(row) {
+ const tableId = row.tableId || this.ids[0];
+ this.$router.push({ path: "/gen/edit", query: { tableId: tableId } });
+ },
+ /** 删除按钮操作 */
+ handleDelete(row) {
+ const tableIds = row.tableId || this.ids;
+ this.$confirm('是否确认删除表编号为"' + tableIds + '"的数据项?', "警告", {
+ confirmButtonText: "确定",
+ cancelButtonText: "取消",
+ type: "warning"
+ }).then(function() {
+ return delTable(tableIds);
+ }).then(() => {
+ this.getList();
+ this.msgSuccess("删除成功");
+ }).catch(function() {});
+ }
+ }
+};
+</script>
\ No newline at end of file
diff --git a/ruoyi-ui/vue.config.js b/ruoyi-ui/vue.config.js
new file mode 100644
index 0000000..5dce8d6
--- /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-visual/pom.xml b/ruoyi-visual/pom.xml
new file mode 100644
index 0000000..0fd93b7
--- /dev/null
+++ b/ruoyi-visual/pom.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi</artifactId>
+ <version>1.0.0</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <modules>
+ <module>ruoyi-monitor</module>
+ </modules>
+
+ <artifactId>ruoyi-visual</artifactId>
+ <packaging>pom</packaging>
+
+ <description>
+ ruoyi-visual图形化管理模块
+ </description>
+
+</project>
diff --git a/ruoyi-visual/ruoyi-monitor/pom.xml b/ruoyi-visual/ruoyi-monitor/pom.xml
new file mode 100644
index 0000000..831b5c2
--- /dev/null
+++ b/ruoyi-visual/ruoyi-monitor/pom.xml
@@ -0,0 +1,74 @@
+<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">
+ <parent>
+ <groupId>com.ruoyi</groupId>
+ <artifactId>ruoyi-visual</artifactId>
+ <version>1.0.0</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>ruoyi-visual-monitor</artifactId>
+
+ <description>
+ ruoyi-visual-monitor监控中心
+ </description>
+
+ <dependencies>
+
+ <!-- SpringBoot Admin -->
+ <dependency>
+ <groupId>de.codecentric</groupId>
+ <artifactId>spring-boot-admin-starter-server</artifactId>
+ <version>${spring-boot-admin.version}</version>
+ </dependency>
+
+ <!-- SpringCloud Ailibaba Nacos -->
+ <dependency>
+ <groupId>com.alibaba.cloud</groupId>
+ <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
+ </dependency>
+
+ <!-- SpringCloud Ailibaba Nacos Config -->
+ <dependency>
+ <groupId>com.alibaba.cloud</groupId>
+ <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
+ </dependency>
+
+ <!-- SpringCloud Netflix Hystrix -->
+ <dependency>
+ <groupId>org.springframework.cloud</groupId>
+ <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
+ </dependency>
+
+ <!-- SpringBoot Web -->
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-web</artifactId>
+ </dependency>
+
+ <!-- Spring Security -->
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-security</artifactId>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>repackage</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
\ No newline at end of file
diff --git a/ruoyi-visual/ruoyi-monitor/src/main/java/com/ruoyi/modules/monitor/RuoYiMonitorApplication.java b/ruoyi-visual/ruoyi-monitor/src/main/java/com/ruoyi/modules/monitor/RuoYiMonitorApplication.java
new file mode 100644
index 0000000..d0f295e
--- /dev/null
+++ b/ruoyi-visual/ruoyi-monitor/src/main/java/com/ruoyi/modules/monitor/RuoYiMonitorApplication.java
@@ -0,0 +1,30 @@
+package com.ruoyi.modules.monitor;
+
+import de.codecentric.boot.admin.server.config.EnableAdminServer;
+import org.springframework.boot.SpringApplication;
+import org.springframework.cloud.client.SpringCloudApplication;
+
+/**
+ * 监控中心
+ *
+ * @author ruoyi
+ */
+@EnableAdminServer
+@SpringCloudApplication
+public class RuoYiMonitorApplication
+{
+ public static void main(String[] args)
+ {
+ SpringApplication.run(RuoYiMonitorApplication.class, args);
+ System.out.println("(♥◠‿◠)ノ゙ 监控中心启动成功 ლ(´ڡ`ლ)゙ \n" +
+ " .-------. ____ __ \n" +
+ " | _ _ \\ \\ \\ / / \n" +
+ " | ( ' ) | \\ _. / ' \n" +
+ " |(_ o _) / _( )_ .' \n" +
+ " | (_,_).' __ ___(_ o _)' \n" +
+ " | |\\ \\ | || |(_,_)' \n" +
+ " | | \\ `' /| `-' / \n" +
+ " | | \\ / \\ / \n" +
+ " ''-' `'-' `-..-' ");
+ }
+}
diff --git a/ruoyi-visual/ruoyi-monitor/src/main/java/com/ruoyi/modules/monitor/config/WebSecurityConfigurer.java b/ruoyi-visual/ruoyi-monitor/src/main/java/com/ruoyi/modules/monitor/config/WebSecurityConfigurer.java
new file mode 100644
index 0000000..0ba5364
--- /dev/null
+++ b/ruoyi-visual/ruoyi-monitor/src/main/java/com/ruoyi/modules/monitor/config/WebSecurityConfigurer.java
@@ -0,0 +1,49 @@
+package com.ruoyi.modules.monitor.config;
+
+import de.codecentric.boot.admin.server.config.AdminServerProperties;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
+
+/**
+ * 监控权限配置
+ *
+ * @author ruoyi
+ */
+@Configuration
+public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter
+{
+ private final String adminContextPath;
+
+ public WebSecurityConfigurer(AdminServerProperties adminServerProperties)
+ {
+ this.adminContextPath = adminServerProperties.getContextPath();
+ }
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception
+ {
+ SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
+ successHandler.setTargetUrlParameter("redirectTo");
+ successHandler.setDefaultTargetUrl(adminContextPath + "/");
+
+ http
+ .headers().frameOptions().disable()
+ .and().authorizeRequests()
+ .antMatchers(adminContextPath + "/assets/**"
+ , adminContextPath + "/login"
+ , adminContextPath + "/actuator/**"
+ , adminContextPath + "/instances/**"
+ ).permitAll()
+ .anyRequest().authenticated()
+ .and()
+ .formLogin().loginPage(adminContextPath + "/login")
+ .successHandler(successHandler).and()
+ .logout().logoutUrl(adminContextPath + "/logout")
+ .and()
+ .httpBasic().and()
+ .csrf()
+ .disable();
+ }
+}
diff --git a/ruoyi-visual/ruoyi-monitor/src/main/resources/banner.txt b/ruoyi-visual/ruoyi-monitor/src/main/resources/banner.txt
new file mode 100644
index 0000000..c0fbab1
--- /dev/null
+++ b/ruoyi-visual/ruoyi-monitor/src/main/resources/banner.txt
@@ -0,0 +1,10 @@
+Spring Boot Version: ${spring-boot.version}
+Spring Application Name: ${spring.application.name}
+ _ _ _
+ (_) (_)| |
+ _ __ _ _ ___ _ _ _ ______ _ __ ___ ___ _ __ _ | |_ ___ _ __
+| '__|| | | | / _ \ | | | || ||______|| '_ ` _ \ / _ \ | '_ \ | || __| / _ \ | '__|
+| | | |_| || (_) || |_| || | | | | | | || (_) || | | || || |_ | (_) || |
+|_| \__,_| \___/ \__, ||_| |_| |_| |_| \___/ |_| |_||_| \__| \___/ |_|
+ __/ |
+ |___/
\ No newline at end of file
diff --git a/ruoyi-visual/ruoyi-monitor/src/main/resources/bootstrap.yml b/ruoyi-visual/ruoyi-monitor/src/main/resources/bootstrap.yml
new file mode 100644
index 0000000..ec9acb3
--- /dev/null
+++ b/ruoyi-visual/ruoyi-monitor/src/main/resources/bootstrap.yml
@@ -0,0 +1,24 @@
+# Tomcat
+server:
+ port: 9100
+
+# Spring
+spring:
+ application:
+ # 应用名称
+ name: ruoyi-monitor
+ profiles:
+ # 环境配置
+ active: dev
+ cloud:
+ nacos:
+ discovery:
+ # 服务注册地址
+ server-addr: 127.0.0.1:8848
+ config:
+ # 配置中心地址
+ server-addr: 127.0.0.1:8848
+ # 配置文件格式
+ file-extension: yml
+ # 共享配置
+ shared-dataids: application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
diff --git a/sql/quartz.sql b/sql/quartz.sql
new file mode 100644
index 0000000..52b6a09
--- /dev/null
+++ b/sql/quartz.sql
@@ -0,0 +1,170 @@
+-- ----------------------------
+-- 1、存储每一个已配置的 jobDetail 的详细信息
+-- ----------------------------
+drop table if exists QRTZ_JOB_DETAILS;
+create table QRTZ_JOB_DETAILS (
+ sched_name varchar(120) not null,
+ job_name varchar(200) not null,
+ job_group varchar(200) not null,
+ description varchar(250) null,
+ job_class_name varchar(250) not null,
+ is_durable varchar(1) not null,
+ is_nonconcurrent varchar(1) not null,
+ is_update_data varchar(1) not null,
+ requests_recovery varchar(1) not null,
+ job_data blob null,
+ primary key (sched_name,job_name,job_group)
+) engine=innodb;
+
+-- ----------------------------
+-- 2、 存储已配置的 Trigger 的信息
+-- ----------------------------
+drop table if exists QRTZ_TRIGGERS;
+create table QRTZ_TRIGGERS (
+ sched_name varchar(120) not null,
+ trigger_name varchar(200) not null,
+ trigger_group varchar(200) not null,
+ job_name varchar(200) not null,
+ job_group varchar(200) not null,
+ description varchar(250) null,
+ next_fire_time bigint(13) null,
+ prev_fire_time bigint(13) null,
+ priority integer null,
+ trigger_state varchar(16) not null,
+ trigger_type varchar(8) not null,
+ start_time bigint(13) not null,
+ end_time bigint(13) null,
+ calendar_name varchar(200) null,
+ misfire_instr smallint(2) null,
+ job_data blob null,
+ primary key (sched_name,trigger_name,trigger_group),
+ foreign key (sched_name,job_name,job_group) references QRTZ_JOB_DETAILS(sched_name,job_name,job_group)
+) engine=innodb;
+
+-- ----------------------------
+-- 3、 存储简单的 Trigger,包括重复次数,间隔,以及已触发的次数
+-- ----------------------------
+drop table if exists QRTZ_SIMPLE_TRIGGERS;
+create table QRTZ_SIMPLE_TRIGGERS (
+ sched_name varchar(120) not null,
+ trigger_name varchar(200) not null,
+ trigger_group varchar(200) not null,
+ repeat_count bigint(7) not null,
+ repeat_interval bigint(12) not null,
+ times_triggered bigint(10) not null,
+ primary key (sched_name,trigger_name,trigger_group),
+ foreign key (sched_name,trigger_name,trigger_group) references QRTZ_TRIGGERS(sched_name,trigger_name,trigger_group)
+) engine=innodb;
+
+-- ----------------------------
+-- 4、 存储 Cron Trigger,包括 Cron 表达式和时区信息
+-- ----------------------------
+drop table if exists QRTZ_CRON_TRIGGERS;
+create table QRTZ_CRON_TRIGGERS (
+ sched_name varchar(120) not null,
+ trigger_name varchar(200) not null,
+ trigger_group varchar(200) not null,
+ cron_expression varchar(200) not null,
+ time_zone_id varchar(80),
+ primary key (sched_name,trigger_name,trigger_group),
+ foreign key (sched_name,trigger_name,trigger_group) references QRTZ_TRIGGERS(sched_name,trigger_name,trigger_group)
+) engine=innodb;
+
+-- ----------------------------
+-- 5、 Trigger 作为 Blob 类型存储(用于 Quartz 用户用 JDBC 创建他们自己定制的 Trigger 类型,JobStore 并不知道如何存储实例的时候)
+-- ----------------------------
+drop table if exists QRTZ_BLOB_TRIGGERS;
+create table QRTZ_BLOB_TRIGGERS (
+ sched_name varchar(120) not null,
+ trigger_name varchar(200) not null,
+ trigger_group varchar(200) not null,
+ blob_data blob null,
+ primary key (sched_name,trigger_name,trigger_group),
+ foreign key (sched_name,trigger_name,trigger_group) references QRTZ_TRIGGERS(sched_name,trigger_name,trigger_group)
+) engine=innodb;
+
+-- ----------------------------
+-- 6、 以 Blob 类型存储存放日历信息, quartz可配置一个日历来指定一个时间范围
+-- ----------------------------
+drop table if exists QRTZ_CALENDARS;
+create table QRTZ_CALENDARS (
+ sched_name varchar(120) not null,
+ calendar_name varchar(200) not null,
+ calendar blob not null,
+ primary key (sched_name,calendar_name)
+) engine=innodb;
+
+-- ----------------------------
+-- 7、 存储已暂停的 Trigger 组的信息
+-- ----------------------------
+drop table if exists QRTZ_PAUSED_TRIGGER_GRPS;
+create table QRTZ_PAUSED_TRIGGER_GRPS (
+ sched_name varchar(120) not null,
+ trigger_group varchar(200) not null,
+ primary key (sched_name,trigger_group)
+) engine=innodb;
+
+-- ----------------------------
+-- 8、 存储与已触发的 Trigger 相关的状态信息,以及相联 Job 的执行信息
+-- ----------------------------
+drop table if exists QRTZ_FIRED_TRIGGERS;
+create table QRTZ_FIRED_TRIGGERS (
+ sched_name varchar(120) not null,
+ entry_id varchar(95) not null,
+ trigger_name varchar(200) not null,
+ trigger_group varchar(200) not null,
+ instance_name varchar(200) not null,
+ fired_time bigint(13) not null,
+ sched_time bigint(13) not null,
+ priority integer not null,
+ state varchar(16) not null,
+ job_name varchar(200) null,
+ job_group varchar(200) null,
+ is_nonconcurrent varchar(1) null,
+ requests_recovery varchar(1) null,
+ primary key (sched_name,entry_id)
+) engine=innodb;
+
+-- ----------------------------
+-- 9、 存储少量的有关 Scheduler 的状态信息,假如是用于集群中,可以看到其他的 Scheduler 实例
+-- ----------------------------
+drop table if exists QRTZ_SCHEDULER_STATE;
+create table QRTZ_SCHEDULER_STATE (
+ sched_name varchar(120) not null,
+ instance_name varchar(200) not null,
+ last_checkin_time bigint(13) not null,
+ checkin_interval bigint(13) not null,
+ primary key (sched_name,instance_name)
+) engine=innodb;
+
+-- ----------------------------
+-- 10、 存储程序的悲观锁的信息(假如使用了悲观锁)
+-- ----------------------------
+drop table if exists QRTZ_LOCKS;
+create table QRTZ_LOCKS (
+ sched_name varchar(120) not null,
+ lock_name varchar(40) not null,
+ primary key (sched_name,lock_name)
+) engine=innodb;
+
+drop table if exists QRTZ_SIMPROP_TRIGGERS;
+create table QRTZ_SIMPROP_TRIGGERS (
+ sched_name varchar(120) not null,
+ trigger_name varchar(200) not null,
+ trigger_group varchar(200) not null,
+ str_prop_1 varchar(512) null,
+ str_prop_2 varchar(512) null,
+ str_prop_3 varchar(512) null,
+ int_prop_1 int null,
+ int_prop_2 int null,
+ long_prop_1 bigint null,
+ long_prop_2 bigint null,
+ dec_prop_1 numeric(13,4) null,
+ dec_prop_2 numeric(13,4) null,
+ bool_prop_1 varchar(1) null,
+ bool_prop_2 varchar(1) null,
+ primary key (sched_name,trigger_name,trigger_group),
+ foreign key (sched_name,trigger_name,trigger_group) references QRTZ_TRIGGERS(sched_name,trigger_name,trigger_group)
+) engine=innodb;
+
+commit;
\ No newline at end of file
diff --git a/sql/ry_20200520.sql b/sql/ry_20200520.sql
new file mode 100644
index 0000000..f49e988
--- /dev/null
+++ b/sql/ry_20200520.sql
@@ -0,0 +1,702 @@
+-- ----------------------------
+-- 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 '菜单类型(M目录 C菜单 F按钮)',
+ visible char(1) default 0 comment '菜单状态(0显示 1隐藏)',
+ status 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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', 'http://localhost:9100/login', '', 1, 'C', '0', '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', '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', 'gen', 'tool/gen/index', 1, 'C', '0', '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', 'http://localhost:8080/swagger-ui.html', '', 1, 'C', '0', '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', 'system/operlog/index', 1, 'C', '0', '0', 'system: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', 'system/logininfor/index', 1, 'C', '0', '0', 'system: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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '0', 'system: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', '0', 'system: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', '0', 'system: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', '0', 'system: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', '0', 'system: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', '0', 'system: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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', '2', '#', '', 1, 'F', '0', '0', 'tool:gen:import', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
+insert into sys_menu values('1059', '预览代码', '114', '4', '#', '', 1, 'F', '0', '0', 'tool:gen:preview', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
+insert into sys_menu values('1060', '生成代码', '114', '5', '#', '', 1, 'F', '0', '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');
+insert into sys_role_menu values ('2', '1060');
+
+-- ----------------------------
+-- 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_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', '');
+
+
+-- ----------------------------
+-- 16、定时任务调度日志表
+-- ----------------------------
+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 = '定时任务调度日志表';
+
+
+-- ----------------------------
+-- 17、通知公告表
+-- ----------------------------
+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', '管理员');
+
+
+-- ----------------------------
+-- 18、代码生成业务表
+-- ----------------------------
+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 = '代码生成业务表';
+
+
+-- ----------------------------
+-- 19、代码生成业务表字段
+-- ----------------------------
+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 = '代码生成业务表字段';
+
+
+-- ----------------------------
+-- 20、客户端配置表
+-- ----------------------------
+drop table if exists sys_oauth_client_details;
+create table sys_oauth_client_details (
+ client_id varchar(255) not null comment '客户端唯一标识',
+ resource_ids varchar(255) default null comment '资源ID标识',
+ client_secret varchar(255) not null comment '客户端安全码',
+ scope varchar(255) not null comment '客户端授权范围',
+ authorized_grant_types varchar(255) not null comment '客户端授权类型',
+ web_server_redirect_uri varchar(255) default null comment '服务器回调地址',
+ authorities varchar(255) default null comment '访问资源所需权限',
+ access_token_validity int(11) default null comment '设定客户端的access_token的有效时间值(秒)',
+ refresh_token_validity int(11) default null comment '设定客户端的refresh_token的有效时间值(秒)',
+ additional_information varchar(4096) default null comment '预留字段',
+ autoapprove tinyint(4) default null comment '是否登录时跳过授权(默认false)',
+ primary key (client_id)
+) engine=innodb auto_increment=1 comment = '客户端配置表';
+
+-- ----------------------------
+-- 初始化-客户端配置表数据
+-- ----------------------------
+insert into sys_oauth_client_details values ('web', '', '$2a$10$y2hKeELx.z3Sbz.kjQ4wmuiIsv5ZSbUQ1ov4BwFH6ccirP8Knp1uq', 'server', 'password,refresh_token', '', NULL, 3600, 7200, NULL, NULL);
+insert into sys_oauth_client_details values ('ruoyi', '', '$2a$10$y2hKeELx.z3Sbz.kjQ4wmuiIsv5ZSbUQ1ov4BwFH6ccirP8Knp1uq', 'server', 'password,client_credentials,refresh_token', '', NULL, 3600, 7200, NULL, NULL);
\ No newline at end of file
diff --git a/sql/ry_config.sql b/sql/ry_config.sql
new file mode 100644
index 0000000..4ef903e
--- /dev/null
+++ b/sql/ry_config.sql
@@ -0,0 +1,205 @@
+DROP DATABASE IF EXISTS `ry-config`;
+
+CREATE DATABASE `ry-config` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
+
+SET NAMES utf8mb4;
+SET FOREIGN_KEY_CHECKS = 0;
+
+USE `ry-config`;
+
+/******************************************/
+/* 表名称 = config_info */
+/******************************************/
+CREATE TABLE `config_info` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
+ `data_id` varchar(255) NOT NULL COMMENT 'data_id',
+ `group_id` varchar(255) DEFAULT NULL,
+ `content` longtext NOT NULL COMMENT 'content',
+ `md5` varchar(32) DEFAULT NULL COMMENT 'md5',
+ `gmt_create` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '创建时间',
+ `gmt_modified` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '修改时间',
+ `src_user` text COMMENT 'source user',
+ `src_ip` varchar(20) DEFAULT NULL COMMENT 'source ip',
+ `app_name` varchar(128) DEFAULT NULL,
+ `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
+ `c_desc` varchar(256) DEFAULT NULL,
+ `c_use` varchar(64) DEFAULT NULL,
+ `effect` varchar(64) DEFAULT NULL,
+ `type` varchar(64) DEFAULT NULL,
+ `c_schema` text,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uk_configinfo_datagrouptenant` (`data_id`,`group_id`,`tenant_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info';
+
+insert into config_info(id, data_id, group_id, content, md5, gmt_create, gmt_modified, src_user, src_ip, app_name, tenant_id, c_desc, c_use, effect, type, c_schema) values
+(1,'application-dev.yml','DEFAULT_GROUP','#请求处理的超时时间\nribbon:\n ReadTimeout: 10000\n ConnectTimeout: 10000\n\n# feign 配置\nfeign:\n hystrix:\n enabled: true\n okhttp:\n enabled: true\n httpclient:\n enabled: false\n client:\n config:\n default:\n connectTimeout: 10000\n readTimeout: 10000\n compression:\n request:\n enabled: true\n response:\n enabled: true\n\n# hystrix 配置\nhystrix:\n command:\n default:\n execution:\n isolation:\n strategy: SEMAPHORE\n thread:\n timeoutInMilliseconds: 60000\n shareSecurityContext: true\n\n# 暴露监控端点\nmanagement:\n endpoints:\n web:\n exposure:\n include: \'*\'\n\n# 认证配置\nsecurity:\n oauth2:\n client:\n client-id: ruoyi\n client-secret: 123456\n scope: server\n resource:\n loadBalanced: true\n token-info-uri: http://ruoyi-auth/oauth/check_token\n ignore:\n urls:\n - /v2/api-docs\n - /actuator/**\n','5d9234a0efea9ae9f8449f8f550788e3','2019-11-29 16:31:20','2020-05-19 18:50:11',NULL,'0:0:0:0:0:0:0:1','','','通用配置','null','null','yaml','null'),
+(2,'ruoyi-gateway-dev.yml','DEFAULT_GROUP','spring: \r\n redis:\r\n host: localhost\r\n port: 6379\r\n password: \r\n cloud:\r\n gateway:\r\n discovery:\r\n locator:\r\n lowerCaseServiceId: true\r\n enabled: true\r\n routes:\r\n # 认证中心\r\n - id: ruoyi-auth\r\n uri: lb://ruoyi-auth\r\n predicates:\r\n - Path=/auth/**\r\n filters:\r\n # 验证码处理\r\n - ImageCodeFilter\r\n - StripPrefix=1\r\n # 代码生成\r\n - id: ruoyi-gen\r\n uri: lb://ruoyi-gen\r\n predicates:\r\n - Path=/code/**\r\n filters:\r\n - StripPrefix=1\r\n # 定时任务\r\n - id: ruoyi-job\r\n uri: lb://ruoyi-job\r\n predicates:\r\n - Path=/schedule/**\r\n filters:\r\n - StripPrefix=1\r\n # 系统模块\r\n - id: ruoyi-system\r\n uri: lb://ruoyi-system\r\n predicates:\r\n - Path=/system/**\r\n filters:\r\n - StripPrefix=1\r\n\r\n# 不校验验证码终端\r\nignore:\r\n clients:\r\n - ruoyi\r\n','8c980ade5250e98121f5207c3a97f35f','2020-05-14 14:17:55','2020-05-19 18:50:24',NULL,'0:0:0:0:0:0:0:1','','','网关模块','null','null','yaml','null'),
+(3,'ruoyi-auth-dev.yml','DEFAULT_GROUP','spring: \r\n main: \r\n allow-bean-definition-overriding: true\r\n datasource:\r\n driver-class-name: com.mysql.cj.jdbc.Driver\r\n url: jdbc:mysql://localhost:3306/ry-cloud?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8\r\n username: root\r\n password: password\r\n redis:\r\n host: localhost\r\n port: 6379\r\n password: \r\n','e3db7475e43b8a83b0247ca8fd77339e','2020-05-14 13:20:49','2020-05-19 18:50:35',NULL,'0:0:0:0:0:0:0:1','','','认证中心','null','null','yaml','null'),
+(4,'ruoyi-monitor-dev.yml','DEFAULT_GROUP','# Spring\r\nspring: \r\n security:\r\n user:\r\n name: ruoyi\r\n password: 123456\r\n boot:\r\n admin:\r\n ui:\r\n title: 若依服务状态监控\r\n','8e49d78998a7780d780305aeefe4fb1b','2020-05-19 15:14:01','2020-05-19 18:50:44',NULL,'0:0:0:0:0:0:0:1','','','监控中心','null','null','yaml','null'),
+(5,'ruoyi-system-dev.yml','DEFAULT_GROUP','# Spring\r\nspring: \r\n datasource:\r\n driver-class-name: com.mysql.cj.jdbc.Driver\r\n url: jdbc:mysql://localhost:3306/ry-cloud?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8\r\n username: root\r\n password: password\r\n\r\n# Mybatis配置\r\nmybatis:\r\n # 搜索指定包别名\r\n typeAliasesPackage: com.ruoyi.system\r\n # 配置mapper的扫描,找到所有的mapper.xml映射文件\r\n mapperLocations: classpath:mapper/**/*.xml\r\n\r\n# swagger 配置\r\nswagger:\r\n title: 系统模块接口文档\r\n license: Powered By ruoyi\r\n licenseUrl: https://ruoyi.vip\r\n authorization:\r\n name: RuoYi OAuth\r\n auth-regex: ^.*$\r\n authorization-scope-list:\r\n - scope: server\r\n description: 客户端授权范围\r\n token-url-list:\r\n - http://localhost:8080/auth/oauth/token\r\n','4b9284d702b9ab819a698708254b0b06','2020-05-14 13:37:04','2020-05-19 18:50:57',NULL,'0:0:0:0:0:0:0:1','','','系统模块','null','null','yaml','null'),
+(6,'ruoyi-gen-dev.yml','DEFAULT_GROUP','# Spring\r\nspring: \r\n datasource:\r\n driver-class-name: com.mysql.cj.jdbc.Driver\r\n url: jdbc:mysql://localhost:3306/ry-cloud?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8\r\n username: root\r\n password: password\r\n\r\n# Mybatis配置\r\nmybatis:\r\n # 搜索指定包别名\r\n typeAliasesPackage: com.ruoyi.gen.domain\r\n # 配置mapper的扫描,找到所有的mapper.xml映射文件\r\n mapperLocations: classpath:mapper/**/*.xml\r\n\r\n# swagger 配置\r\nswagger:\r\n title: 代码生成接口文档\r\n license: Powered By ruoyi\r\n licenseUrl: https://ruoyi.vip\r\n authorization:\r\n name: RuoYi OAuth\r\n auth-regex: ^.*$\r\n authorization-scope-list:\r\n - scope: server\r\n description: 客户端授权范围\r\n token-url-list:\r\n - http://localhost:8080/auth/oauth/token\r\n\r\n# 代码生成\r\ngen: \r\n # 作者\r\n author: ruoyi\r\n # 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool\r\n packageName: com.ruoyi.system\r\n # 自动去除表前缀,默认是false\r\n autoRemovePre: false\r\n # 表前缀(生成类名不会包含表前缀,多个用逗号分隔)\r\n tablePrefix: sys_\r\n','aa7e94e2abbdeb408bd8981391ab82f8','2020-05-14 13:54:50','2020-05-19 18:51:11',NULL,'0:0:0:0:0:0:0:1','','','代码生成','null','null','yaml','null'),
+(7,'ruoyi-job-dev.yml','DEFAULT_GROUP','# Spring\r\nspring: \r\n datasource:\r\n driver-class-name: com.mysql.cj.jdbc.Driver\r\n url: jdbc:mysql://localhost:3306/ry-cloud?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8\r\n username: root\r\n password: password\r\n\r\n# Mybatis配置\r\nmybatis:\r\n # 搜索指定包别名\r\n typeAliasesPackage: com.ruoyi.job.domain\r\n # 配置mapper的扫描,找到所有的mapper.xml映射文件\r\n mapperLocations: classpath:mapper/**/*.xml\r\n\r\n# swagger 配置\r\nswagger:\r\n title: 定时任务接口文档\r\n license: Powered By ruoyi\r\n licenseUrl: https://ruoyi.vip\r\n authorization:\r\n name: RuoYi OAuth\r\n auth-regex: ^.*$\r\n authorization-scope-list:\r\n - scope: server\r\n description: 客户端授权范围\r\n token-url-list:\r\n - http://localhost:8080/auth/oauth/token\r\n','2904b375372b13f52baed5be2e497b21','2020-05-14 13:58:46','2020-05-19 18:49:56',NULL,'0:0:0:0:0:0:0:1','','','定时任务','null','null','yaml','null');
+
+
+/******************************************/
+/* 表名称 = config_info_aggr */
+/******************************************/
+CREATE TABLE `config_info_aggr` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
+ `data_id` varchar(255) NOT NULL COMMENT 'data_id',
+ `group_id` varchar(255) NOT NULL COMMENT 'group_id',
+ `datum_id` varchar(255) NOT NULL COMMENT 'datum_id',
+ `content` longtext NOT NULL COMMENT '内容',
+ `gmt_modified` datetime NOT NULL COMMENT '修改时间',
+ `app_name` varchar(128) DEFAULT NULL,
+ `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uk_configinfoaggr_datagrouptenantdatum` (`data_id`,`group_id`,`tenant_id`,`datum_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='增加租户字段';
+
+
+/******************************************/
+/* 表名称 = config_info_beta */
+/******************************************/
+CREATE TABLE `config_info_beta` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
+ `data_id` varchar(255) NOT NULL COMMENT 'data_id',
+ `group_id` varchar(128) NOT NULL COMMENT 'group_id',
+ `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
+ `content` longtext NOT NULL COMMENT 'content',
+ `beta_ips` varchar(1024) DEFAULT NULL COMMENT 'betaIps',
+ `md5` varchar(32) DEFAULT NULL COMMENT 'md5',
+ `gmt_create` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '创建时间',
+ `gmt_modified` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '修改时间',
+ `src_user` text COMMENT 'source user',
+ `src_ip` varchar(20) DEFAULT NULL COMMENT 'source ip',
+ `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uk_configinfobeta_datagrouptenant` (`data_id`,`group_id`,`tenant_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info_beta';
+
+/******************************************/
+/* 表名称 = config_info_tag */
+/******************************************/
+CREATE TABLE `config_info_tag` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
+ `data_id` varchar(255) NOT NULL COMMENT 'data_id',
+ `group_id` varchar(128) NOT NULL COMMENT 'group_id',
+ `tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id',
+ `tag_id` varchar(128) NOT NULL COMMENT 'tag_id',
+ `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
+ `content` longtext NOT NULL COMMENT 'content',
+ `md5` varchar(32) DEFAULT NULL COMMENT 'md5',
+ `gmt_create` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '创建时间',
+ `gmt_modified` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '修改时间',
+ `src_user` text COMMENT 'source user',
+ `src_ip` varchar(20) DEFAULT NULL COMMENT 'source ip',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uk_configinfotag_datagrouptenanttag` (`data_id`,`group_id`,`tenant_id`,`tag_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info_tag';
+
+/******************************************/
+/* 表名称 = config_tags_relation */
+/******************************************/
+CREATE TABLE `config_tags_relation` (
+ `id` bigint(20) NOT NULL COMMENT 'id',
+ `tag_name` varchar(128) NOT NULL COMMENT 'tag_name',
+ `tag_type` varchar(64) DEFAULT NULL COMMENT 'tag_type',
+ `data_id` varchar(255) NOT NULL COMMENT 'data_id',
+ `group_id` varchar(128) NOT NULL COMMENT 'group_id',
+ `tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id',
+ `nid` bigint(20) NOT NULL AUTO_INCREMENT,
+ PRIMARY KEY (`nid`),
+ UNIQUE KEY `uk_configtagrelation_configidtag` (`id`,`tag_name`,`tag_type`),
+ KEY `idx_tenant_id` (`tenant_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_tag_relation';
+
+/******************************************/
+/* 表名称 = group_capacity */
+/******************************************/
+CREATE TABLE `group_capacity` (
+ `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+ `group_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Group ID,空字符表示整个集群',
+ `quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值',
+ `usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量',
+ `max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值',
+ `max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数,,0表示使用默认值',
+ `max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值',
+ `max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量',
+ `gmt_create` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '创建时间',
+ `gmt_modified` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '修改时间',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uk_group_id` (`group_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='集群、各Group容量信息表';
+
+/******************************************/
+/* 表名称 = his_config_info */
+/******************************************/
+CREATE TABLE `his_config_info` (
+ `id` bigint(64) unsigned NOT NULL,
+ `nid` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ `data_id` varchar(255) NOT NULL,
+ `group_id` varchar(128) NOT NULL,
+ `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
+ `content` longtext NOT NULL,
+ `md5` varchar(32) DEFAULT NULL,
+ `gmt_create` datetime NOT NULL DEFAULT '2010-05-05 00:00:00',
+ `gmt_modified` datetime NOT NULL DEFAULT '2010-05-05 00:00:00',
+ `src_user` text,
+ `src_ip` varchar(20) DEFAULT NULL,
+ `op_type` char(10) DEFAULT NULL,
+ `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
+ PRIMARY KEY (`nid`),
+ KEY `idx_gmt_create` (`gmt_create`),
+ KEY `idx_gmt_modified` (`gmt_modified`),
+ KEY `idx_did` (`data_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='多租户改造';
+
+
+/******************************************/
+/* 表名称 = tenant_capacity */
+/******************************************/
+CREATE TABLE `tenant_capacity` (
+ `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+ `tenant_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Tenant ID',
+ `quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值',
+ `usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量',
+ `max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值',
+ `max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数',
+ `max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值',
+ `max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量',
+ `gmt_create` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '创建时间',
+ `gmt_modified` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '修改时间',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uk_tenant_id` (`tenant_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='租户容量信息表';
+
+
+CREATE TABLE `tenant_info` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
+ `kp` varchar(128) NOT NULL COMMENT 'kp',
+ `tenant_id` varchar(128) default '' COMMENT 'tenant_id',
+ `tenant_name` varchar(128) default '' COMMENT 'tenant_name',
+ `tenant_desc` varchar(256) DEFAULT NULL COMMENT 'tenant_desc',
+ `create_source` varchar(32) DEFAULT NULL COMMENT 'create_source',
+ `gmt_create` bigint(20) NOT NULL COMMENT '创建时间',
+ `gmt_modified` bigint(20) NOT NULL COMMENT '修改时间',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uk_tenant_info_kptenantid` (`kp`,`tenant_id`),
+ KEY `idx_tenant_id` (`tenant_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='tenant_info';
+
+CREATE TABLE users (
+ username varchar(50) NOT NULL PRIMARY KEY,
+ password varchar(500) NOT NULL,
+ enabled boolean NOT NULL
+);
+
+CREATE TABLE roles (
+ username varchar(50) NOT NULL,
+ role varchar(50) NOT NULL
+);
+
+INSERT INTO users (username, password, enabled) VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', TRUE);
+
+INSERT INTO roles (username, role) VALUES ('nacos', 'ROLE_ADMIN');
--
Gitblit v1.9.3