自己怎么开发网站,百度搜国外服务器Wordpress,近期的新新闻,做动态图网站有哪些#x1f4d5;开源风云系列 #x1f34a;本系列将从开源名将若依出发#xff0c;探究优质开源项目脚手架汲取编程之道。 #x1f349;从不分离版本开写到前后端分离版#xff0c;再到微服务版本#xff0c;乃至其中好玩的一系列增强Plus操作。 #x1f348;希望你具备如下…开源风云系列 本系列将从开源名将若依出发探究优质开源项目脚手架汲取编程之道。 从不分离版本开写到前后端分离版再到微服务版本乃至其中好玩的一系列增强Plus操作。 希望你具备如下技术栈 SpringSpringMVCMybatis/Mybatis-plusThymeleafSpringBootShiroSpringSecuritySpringCloud云服务器相关知识 本篇是分离版第一篇 文章同时同步到我的个人站点欢迎来访 国内https://www.linxiaoqin.netlify.app国际https://www.linxiaoqin.vercel.app 目录 开源风云系列1、若依践行SpringSecurity1.1、Apifox如何调接口 2、RBAC2.1、权限2.2、何时获得权限2.3、页面权限的获取2.4、按钮权限 3、第三方登录4、新建模块5、前端通用方法5.1、$tab5.1.1、打开新标签页5.1.2、修改标签页5.1.3、关闭标签5.1.4、刷新标签5.1.5、关闭所有页签5.1.6、关闭左侧页签 5.2、$modal5.3、$auth5.4、$download5.4.1、根据名称下载upload路径下的文件 6、Https实现 1、若依践行SpringSecurity
若依前后端分离采用SpringSecurity来做认证和授权操作使用SpringSecurity我们只需要authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password))
即:
认证管理器调用认证方法将我们的用户名和密码封装成UsernamePasswordAuthenticationToken传入认证方法认证管理器调用认证方法执行后会自动调用UserDetailsServiceImpl.loadUserByUsername()方法我们自己写的类实现了UserDetailsService接口并重写loadUserByUsername方法 我们简单 debug 认证方法一下看看可以发现的是
在用户登录的时候对于任何异常都会记录进日志然后获取当前的登录对象通过它来生成token 其中recordLoginInfo(loginUser.getUserId())是记录登录的信息的包括登录的 ip、登录时间然后将信息更新到数据库里面我们可以进去方法里面查看 那么最后在tokenService.createToken(loginUser)生成token是怎么生成的呢
首先生成一个uuid将uuid设置给LoginUser的token字段这个uuid其实也就是tokenidsetUserAgent(loginUser)设置用户代理这个用户代理干了什么呢 这个设置用户代理方法其实就是获得了很多信息包括操作系统、浏览器信息、ip等然后将其更新进数据库中。 最后执行refreshToken(loginUser)刷新令牌有效期首先设置登录时间为系统当前时间设置过期时间expireTime时间是读取yml配置的信息然后将键值存入 redis 中键是userKey,值是loginUser。 再回头来看我们创建令牌的代码创建一个HashMap将键值对login_user_keytoken存入HashMap然后创建token并将HashMap传入。 我们进入createToken方法设置主体信息为claims也就是我们上面HashMap的键值对然后设置签名方式加密方式为HS512密钥secret通过yml配置文件读取密钥随便填即可。 1.1、Apifox如何调接口
现在有一个这样的问题遇到认证失败但是自己想用postman、apifox、apipost等软件就想要调通API怎么办可以使用超级管理员的token当我们登录超级管理员随便请求一个页面就可以在两个地方发现token
一个是请求头的Authorization另一个是Cookie里面 包括Swagger也需要添加token才能调试的通 2、RBAC
RBAC是一种基于角色权限控制进而控制用户的权限。也就是给用户角色给角色权限
具体而言对系统操作的各种权限不是直接授予具体的用户而是在用户集合与权限集合之间建立一个角色集合。每一种角色对应一组相应的权限。一旦用户被分配了适当的角色后该用户就拥有此角色的所有操作权限。
这样做的好处是不必在每次创建用户时都进行分配权限的操作只要分配用户相应的角色即可而且角色的权限变更比用户的权限变更要少得多这样将简化用户的权限管理减少系统的开销。
2.1、权限
页面权限就是进入某个页面所需要的权限按钮权限则是能点击某个按钮所需要的权限
对于权限在程序中往往表现为一个字符串比如说system:user:add表示对用户具有增加权限当然也可以说dsadsaddsadsadsa表示对用户有增加权限字符串是随意定的但是为了符合人类的想法要做到尽量的见名知意。
对于若依而言在系统管理→菜单管理对每一个菜单对应了某种权限如下图所示 下面我们以岗位管理为例子说明岗位管理的每个字符串的作用岗位管理的权限字符串如上上图的岗位管理对应的字符串为system:post:list该字符串的意思是有了这个字符串才能查看岗位管理的菜单。
system:post:query 字符串表示对单个岗位详情的查询权限system:post:add 表示增权限system:post:edit 表示修改权限system:post:remove 表示删岗位权限system:post:export 表示导出岗位数据Excel文件的权限
岗位管理的system:post:list 是页面权限如果没有该权限连菜单都看不到自然页面也看不到。其他的都是按钮权限 按钮权限在前端页面的表现就是能不能看到按钮在后端的表现就是能不能执行按钮对应的controller的增删改查方法。 在若依中页面菜单、目录菜单和按钮菜单基本上都是存在数据库中首页、404等特殊页面除外在系统管理→菜单管理可以填写每一个菜单对应的权限字符串不过目前目录菜单不能添加权限字符串。 当我们在菜单管理写好权限字符串之后在角色管理我们可以添加角色并把相关的权限赋予给角色,然后给用户分配角色。 [!NOTE] 给用户赋予角色给角色赋予权限。 2.2、何时获得权限
若依用户是在 登录的时候就在数据库得到了属于自己的权限并将其存在了 redis 中。 我们来看一下代码在用户验证处理UserDetailsServiceImpl类中创建了LoginUser并且传入了对应的权限。 permissionService.getMenuPermission(user)则是根据当前登录的用户查询该用户的权限,转到方法进去看看 从上面的代码可以看到如果是超级管理员就给权限字符串为*:*:*那么什么样的人是超级管理员呢 userId1的用户就是超级管理员,也就是说超级管理员是代码写死的前端判断字符串也要先看看权限字符串是不是*:*:*。如果不是超级管理员代码是执行了perms.addAll(menuService.selectMenuPermsByUserId(user.getUserId())),也就是获得当前用户的userId拿着userId去数据库查询权限。 查看Service层的代码可以发现调用Mapper去数据库查询权限迭代查询的每个权限并且split分割了一下通过,逗号分隔所以我们写权限的时候可以这样写system:user:add,system:user:edit,可以在一个菜单的权限标识写好几个权限字符串用逗号分割。
话说回来去数据库怎么查询的权限呢我们看到传入的只有userId查看SQL语句并手动在 navicat 运行userid2的用户权限SQL 可以看到SQL实际上就是不断在左连接所谓左连接通俗而言就是左边表展示全部数据右边表若匹配成功就连接没有匹配成功就右边表空着。 那么这里就涉及到一个问题当我用超级管理员修改了角色的权限对于已经登录的和该角色相关联的用户是不是立刻生效呢 其实对于页面权限是立刻生效的但是对于按钮权限并不是立刻生效的而是要重新登录重新从数据库获取权限然后再存入Redis 2.3、页面权限的获取
当用户登录完毕之后左侧菜单栏展示出来了大量的菜单对于不同权限的用户展示的菜单不尽相同。那么左侧菜单是怎么来的呢这就需要打开Layout组件然后找到侧边栏组件如下 Layout组件的菜单栏组件是Sidebar,里面有一个ElMenu组件对应整个菜单整体SidebarItem全部都是一级菜单,ElSubmenu 对应我们的二级菜单整体,里面的SidebarItem组件为具体的每一个二级菜单。
ElMenu # 整体菜单-- SidebarItem # 一级菜单-- ElSubmenu # 二级菜单如下图我们可以发现ElMenu中的菜单项的迭代数据来自变量sidebarRouters sidebarRouters变量的数据一定是后端查询来的菜单数据。我们继续深入它是vuex的getters 查看Vuex的目录下的 store/getters.js
sidebarRouters:state state.permission.sidebarRouters,可以发现sidebarRouters 的数据是从 permission.js中管理的我们进去permission.js中查看发现sidebarRouters初始化是空的是被SET_SIDEBAR_ROUTERS方法赋值的 我们继续深入SET_SIDEBAR_ROUTERS方法会发现这么一句代码
constantRoutes.concat(sidebarRoutes)也就是我们的sidebarRouters 的数据是由constantRoutes拼接了sidebarRoutes 其中constantRoutes点进去发现其实它就是公共路由不存在数据库中任何角色都可以有 从数据库中取的sidebarRoutes数据其实是请求后端返回的data数据 ![NOTE] sidebarRouters的数据是来自两部分: 一部分来自getRouters方法向后端发请求返回的res的data经过filterAsyncRouter方法过滤后的路由另一部分来自constantRoutesconstantRoutes是写死的来自router文件夹下的index.js文件 前端点击岗位管理的时候调用了岗位管理的system/post/list接口,调用该接口是需要权限的并且权限为system/post/list,每一个接口的调用需要什么权限我们都可以在上面的注解可以看到 ![NOTE] PreAuthorize注解是SpringSecurity框架的注解注解的value值所需要填写一个表达式如果表达式计算的结果是true那么就允许访问对应的接口如果表达式计算的结果是false则表示没有权限。 当我们需要用到PreAuthorize注解先需要写另一个注解来让PreAuthorize注解能生效ry程序在SecurityConfig类已经写了 EnableGlobalMethodSecurity(prePostEnabled true, securedEnabled true) 表示**PreAuthorize和PostAuthorize注解要不要启用** PreAuthorize表示在执行方法前验证权限PostAuthorize表示在执行方法后验证权限。一般用PreAuthorize就够了。 刚刚我们说到PreAuthorize的value值写SpEL表达式并且返回值为true则可以执行方法返回值为false则表示不能执行方法。现在我们来分析SpEL表达式。SpEL表达式能写方法调用ry程序就是写的方法表达式。岗位管理的表达式如下
PreAuthorize(ss.hasPermi(system:post:list))SpEL表达式可以通过来引用bean,所以ss在引用名为ss的Bean然后调用该Bean的hasPermi方法并传入了参数。ss的bean为: 综上所述表达式ss.hasPermi(system:post:list) 的意思是调用下面的方法并传参数为system:post:list,如下 2.4、按钮权限
按钮权限在前端的表现为vue组件、HTML标签是否显示。在ry程序中最常用的方式就是控制按钮显示还是不显示如下 也就是说当用户具有system:post:add权限时新增的el-button组件才会显示。
v-hasPermi是一个新指令vue本身自身没有的那么只能是ry自己写的一个新指令。ry所有自定义指令在src/directive下在main.js引入了src/directive/index.js
import directive from ./directive // directive
Vue.use(directive)自定义指令是以插件的形式包装的Vue.use方法会自动执行插件directive的install方法 install方法中我们看到v-hasPermi被注册。 可以看出对象里面只有一个方法inserted也就是在被绑定元素插入父节点时调用这里正好可以判断有没有权限有就插入没有就直接移除dom即可。
3、第三方登录
当今社会微信登录、QQ登录、抖音登录等等三方登录已经层出不穷学会三方登录势在必行。微信登录要认证开发者必须为企业个人不行而且还要交300块钱。一听到钱白嫖党的钱是不可能被赚的不学微信登录。QQ登录也要申请、微博登录也要申请。
还好Gitee给力申请轻轻松松谁都能轻松让Gitee作为第三方登录JustAuth能让我们第三方登录写少一些代码它包装了国内外30多种三方登录。 使用教程这应该是史上最全的整合第三方登录的开源库 使用JustAuth总共分三步这三步也适合于JustAuth支持的任何一个平台
申请注册第三方平台的开发者账号 - 设置 - 第三方应用 创建应用 应用名称 一般填写自己的网站名称即可 应用描述 一般填写自己的应用描述即可 应用主页 填写自己的网站首页地址 应用回调地址 重点应用回调不能乱填当我们gitee登录成功之后gitee会自动跳转到应用回调地址并且gitee会带上code利用code可以得到所登录gitee用户信息。 权限 根据页面提示操作默认勾选第一个就行。
以上信息输入完成后点击确定按钮创建应用。创建完成后点击进入应用详情页可以看到应用的密钥等信息
引入依赖
!--JustAuth第三方登录--
dependencygroupIdme.zhyd.oauth/groupIdartifactIdJustAuth/artifactIdversion${justAuth.version}/version
/dependency我们可以在根的pom.xml引入必须要在framework包下继续引入但是不用带版本号会自动继承根的pom.xml否则Maven一直报错 接下来我们改 login.vue
!-- Gitee登录 --
div stylewidth: 32px;height: 32px;margin-top: 5px;cursor: pointer; title利用Gitee登录 clickgiteeLoginimg styleheight: 100%;width: 100%; src../assets/logo/profile.jpg
/div我们添加的代码如图clickgiteeLogin点击事件的方法是giteeLogin 页面呈现的样子是 我们看一下点击事件giteeLogin,将其补充在 methods 下
// Gitee登录
giteeLogin() {PreLoginByGitee().then(res {Cookies.set(user-uuid, res.uuid)window.location res.authorizeUrl})
},我们在src/api/login.js里面写入PreLoginByGitee方法
// 获得跳转到Gitee登录的地址
export function PreLoginByGitee() {return request({url: /PreLoginByGitee,headers: {isToken: false},method: get,})
}对应的后端接口 RestController
public class GiteeLoginController {// 创建授权requestGetMapping(/PreLoginByGitee)public AjaxResult PreLoginByGitee() {AjaxResult ajax AjaxResult.success();AuthRequest authRequest new AuthGiteeRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret).redirectUri(redirectUri).build());String uuid IdUtils.fastUUID();String authorizeUrl authRequest.authorize(uuid);//返回给前端一个跳转路径url和uuidajax.put(authorizeUrl, authorizeUrl);ajax.put(uuid, uuid);return ajax;}
}这样就OK了但是后端接口还得放行 我们点击同意授权就会调用我们的回调地址http://localhost/callback ,并且带上code和state也就是会跳转到http://localhost/callbackcodexxxstatexxx,这个 state 其实就是 uuid我们在前端创建路由让前端回调到我们的loginByGitee.vue组件
// Gitee
{path: /callback,component: () import(/views/loginByGitee),hidden: true
},同时我们也准备好了loginByGitee.vue, 里面做的操作就是先从 cookie 中取出 uuid然后将 uuid 和 路由中的 code封装成一个请求体去调用LoginByGitee 方法登录一旦登录成功后就重定向到/首页。 [!NOTE] 其实 uuid 也可以不从cookie中拿到可以从路由中拿到this.$route.query.state templatediv v-loadingloading styleheight: 100%;width: 100%;正在加载中.../div
/templatescriptimport Cookies from js-cookie;export default {name: loginByGitee,data() {return {loading: true}},// 立即执行的钩子函数mounted() {this.loading true;// 从 cookie 中取出 uuidconsole.log(uuid, Cookies.get(user-uuid))// 封装请求体const formBody {uuid: Cookies.get(user-uuid),code: this.$route.query.code}// vuex中调用actions的方法this.$store.dispatch(LoginByGitee, formBody).then(() {this.$router.push({path: this.redirect || /}).catch(() {});}).catch(() {this.loading false;});}
}
/scriptstyle scoped/style记得前端放行/callback路由 所以需要在 actions 中定义 LoginByGitee 方法LoginByGitee 通过提交 commit 来更新Vuex中store的状态并且传递 body 上方封装的请求体将操作放在一个 Promise 中并且在成功或者失败之后调用对应的 resolve 或 reject。 // Gitee登录
LoginByGitee({commit}, body) {return new Promise((resolve, reject) {loginByGitee(body.code, body.uuid).then(res {setToken(res.token)commit(SET_TOKEN, res.token)resolve()}).catch(error {reject(error)})})
},里面才真正调用了loginByGitee方法所以我们在 src/api/login.js补充方法
// Gitee登录
export function loginByGitee(code, uuid) {const data {code,source: Gitee,uuid}return request({url: /loginByGitee,headers: {isToken: false},method: post,data: data})
}前端调用的后端的/loginByGitee方法,所以我们需要在后端放行/loginByGitee方法 /*** 真正登录逻辑* param loginByOtherSourceBody* return*/
PostMapping(/loginByGitee)
public AjaxResult loginByGitee(RequestBody LoginByOtherSourceBody loginByOtherSourceBody) {AjaxResult ajax AjaxResult.success();String token sysLoginService.loginByOtherSource(loginByOtherSourceBody.getCode(), loginByOtherSourceBody.getSource(), loginByOtherSourceBody.getUuid());ajax.put(Constants.TOKEN, token);return ajax;
}这里需要创建一个LoginByOtherSourceBody类并且加上getter和setter方法
public class LoginByOtherSourceBody {private String code;private String source;private String uuid;
}并且sysLoginService.loginByOtherSource Service层是没有这个方法的加上。
public String loginByOtherSource(String code, String source, String uuid) {//先到数据库查询这个人曾经有没有登录过没有就注册// 创建授权requestAuthRequest authRequest new AuthGiteeRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret).redirectUri(http://localhost/callback).build());// Gitee登录成功AuthResponseAuthUser login authRequest.login(AuthCallback.builder().state(uuid).code(code).build());System.out.println(login);//先查询数据库有没有该用户从登录成功的login中拿到登录的信息AuthUser authUser login.getData();SysUser sysUser new SysUser();sysUser.setUserName(authUser.getUsername());sysUser.setSource(authUser.getSource());ListSysUser sysUsers userService.selectUserListNoDataScope(sysUser);if (sysUsers.size() 1) {throw new ServiceException(第三方登录异常账号重叠);} else if (sysUsers.size() 0) {//相当于注册sysUser.setNickName(authUser.getNickname());sysUser.setAvatar(authUser.getAvatar());sysUser.setEmail(authUser.getEmail());sysUser.setRemark(authUser.getRemark());userService.registerUserAndGetUserId(sysUser);AsyncManager.me().execute(AsyncFactory.recordLogininfor(sysUser.getUserName(), Constants.REGISTER,MessageUtils.message(user.register.success)));} else {sysUser sysUsers.get(0);}AsyncManager.me().execute(AsyncFactory.recordLogininfor(sysUser.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message(user.login.success)));//注册成功或者是已经存在的用户LoginUser loginUser new LoginUser(sysUser.getUserId(), sysUser.getDeptId(), sysUser, permissionService.getMenuPermission(sysUser));recordLoginInfo(loginUser.getUserId());// 生成tokenreturn tokenService.createToken(loginUser);
}同时我们在三方登录的时候还需要维护一个source字段表示你是通过QQ、Github、Gitee还是哪个来登录的。
所以我们在sysUser类里面加上source字段,并且生成对应的 getter 和 setter数据库表sys_user表中也需要加 source 字段。
/** 登录源 */
private String source;同时 userService.selectUserListNoDataScope Service层没有这个方法 所以需要写一个查询方法在ISysUserService,SysUserServiceImpl中 同时Mapper.xml里面也需要加上我们的source字段涉及到的查询sql语句我们都可以进行加啦 我们再来看我们三方登录的逻辑根据用户名 username 和 源source 在数据库进行查询如果查到1条以上那么说明第三方登录异常账号重叠了如果1条也查不到那么相当于是在注册了这里重新写了一个注册方法userService.registerUserAndGetUserId 因为我们需要拿到注册的用户的ID 其中我们自己的注册用户信息返回用户ID如下 我们来看看效果,登录进去后头像不显示很明显我们的头像是一个 http请求它自动加上了dev-api前缀 所以我们修改一下获取用户信息的头像逻辑 [!NOTE] 至此我们三方登录正式完成梳理一下 首先去前端写一个 Gitee 的按钮给按钮点击事件点击按钮就执行PreLoginByGitee方法这个方法会去请求后端/PreLoginByGitee接口后端会生成 url也就是跳转到Gitee登录的那个 url这样点击按钮就可以跳转到 Gitee 的登录认证的窗口了认证成功会回调到路由/callback这个路由又跳转到LoginByGitee.vue组件这个组件开始加载中转圈圈然后准备code、uuid直接去请求后端LoginByGitee接口。登录成功后我们可以拿到登录用户的所有数据比如 Gitee 的名字、Gitee的头像等然后我们将这些数据与数据库进行比对通过用户名username和source源进行比对如果没有比对成功的那么就直接使用这个用户数据进行注册。如果有1条比对成功的我们直接拿出来进行登录即可。 4、新建模块
对准项目根文件右键新建选择模块 然后新建模块注意父模块要选ruoyi我这里是因为更改了包名新建模块我的叫KuangStudy_Vue-core [!NOTE] 需要注意的是 我们新建的模块需要在根的 pom.xml下引用 并且需要在admin模块下引入这样的话是直接继承根的依赖就不用写版本号了 现在已经有了新的模块了那么我需要添加我自己的代码有什么注意事项吗 因为RuoYiApplication在com.ruoyi包下所以我们也需要手动建立com.ruoyi包不然自己写的类无法注入Spring容器。 当我们自己新建的模块需要依赖ruoyi-system和ruoyi-framework的时候只需要引入ruoyi-framework即可因为ruoyi-framework里面引入了ruoyi-system。 创建的新模块没有引入spring而spring都在common包下所以必须引入common包 接下来就可以写自己的方法了我这里写了一个控制层接口 这是后端的接口所以得访问http://localhost:8080/kuang,如果通过前端访问的话需要走前端的代理也就是需要访问的url是http://localhost:80/dev-api/kuang。 [!NOTE] 思考我们为什么在控制层方法上添加了Anonymous注解不用之前的token就可以访问到方法了 回答Anonymous注解放到类上该类所有方法都可以匿名访问放到方法上那么就该方法可以被匿名访问。 5、前端通用方法
所谓通用方法就是随时能调用的方法。例如我们的Vue的methods中随时可写
this.$tab.openPage(用户管理, /system/user);
this.$modal.msg(默认反馈);
this.$auth.hasPermi(system:user:add);
this.$auth.hasRole(admin);
this.$cache.local.set(key, local value)
this.$download.name(name);这些方法为什么可以随时调用呢是因为若依准备了插件在main.js中引入了plugins,并且使用Vue.use(plugins) Vue.use 会去调用 plugins 的 install方法给Vue原型 prototype 加上了 $xxx所以在任意的组件实例里面都可以取到了。 5.1、$tab
5.1.1、打开新标签页
可以在方法中调用this.$tab.openPage(title,url)来打开新的标签页标签页的标题是title要打开的标签url是url
templatedivh1打开标签页/h1el-button typeprimary clickopenNotice通知公告1/el-buttonel-button typeprimary clickopenNoticeAndWindows通知公告2/el-button/div
/templatescript
export default {name: kuang,methods: {// 单纯打开新页面openNotice() {this.$tab.openPage(打开的通知公告, /system/notice);},// 打开页面之后并且做一点其他事情。openNoticeAndWindows() {this.$tab.openPage(打开的通知公告并且弹窗, /system/notice).then(() {this.$modal.msg(我是弹窗)})}}}
/scriptstyle/style如上代码我们给两个按钮加了两个方法分别是单纯打开一个新页面新标签页的标题是 打开的通知公告另一个是打开标签页并且提示弹框 我们看看若依封装的openPage方法 从上面的源码可以看出做了两件事
调用Vuex的tagsView/addView方法并传参数obj。**该方法是为了把新打开的页签添加到Vuex维护的页签数组visitedViews。**当添加成功组件TagsView的计算属性visitedViews变化页面重新渲染页签自然多一个。第二件事就是将路由做简单的跳转。
5.1.2、修改标签页
this.$tab.updatePage可以修改标签
templatedivh1打开标签页/h1el-button typeprimary clickopenNotice通知公告1/el-buttonel-button typeprimary clickopenNoticeAndWindows通知公告2/el-buttonel-divider/el-dividerh1修改标签页/h1el-button typeinfo clickupdateSelf修改自己标签/el-buttonel-button typeinfo clickupdateOther修改其他标签/el-button/div
/templatescript
export default {name: kuang,methods: {// 单纯打开新页面openNotice() {this.$tab.openPage(打开的通知公告, /system/notice);},// 打开页面之后并且做一点其他事情。openNoticeAndWindows() {this.$tab.openPage(打开的通知公告并且弹窗, /system/notice).then(() {this.$modal.msg(我是弹窗)})},updateSelf() {//修改自己const obj Object.assign({}, this.$route, {title: 自定义标题})this.$tab.updatePage(obj);},updateOther() {// 修改别的标签页const obj Object.assign({}, {path: /system/notice}, {title: 自定义标题222})this.$tab.updatePage(obj).then(() {this.$modal.msg(修改页签完毕)})},}}
/scriptstyle/style5.1.3、关闭标签
this.$tab.closeOpenPage :关闭当前页签并且打开新页面this.$tab.closePage : 关闭当前页签并回到首页
//关闭当前并且打开新页面
closeAndOpen() {const obj {path: /system/user};this.$tab.closeOpenPage(obj);
},
//关闭当前并回到首页
closeAndGoIndex() {this.$tab.closePage();
},
//关闭指定的页面
closeSpecifyPage() {const obj {path: /system/user};this.$tab.closePage(obj);
}5.1.4、刷新标签
this.$tab.refreshPage() 刷新标签
//刷新当前页签
refreshPage() {this.$tab.refreshPage();
}刷新页签相当于页面关闭重新打开。
5.1.5、关闭所有页签
this.$tab.closeAllPage() : 关闭所有页签
//关闭所有页签
closeAll() {this.$tab.closeAllPage();
},5.1.6、关闭左侧页签
this.$tab.closeLeftPage();const obj { path: /system/user, name: User };
this.$tab.closeLeftPage(obj);this.$tab.closeLeftPage(obj).then(() {// 执行结束的逻辑
})[!NOTE] 抛砖引玉更多请查看官方文档,文档的东西更详细 5.2、$modal
$modal对象用于做消息提示、通知提示、对话框提醒、二次确认、遮罩等。
templatedivh1打开标签页/h1el-button typeprimary clickopenNotice通知公告1/el-buttonel-button typeprimary clickopenNoticeAndWindows通知公告2/el-buttonel-divider/el-dividerh1修改标签页/h1el-button typeinfo clickupdateSelf修改自己标签/el-buttonel-button typeinfo clickupdateOther修改其他标签/el-buttonel-divider/el-dividerh1消息提示弹窗/h1el-button typetext clickmsg反馈信息/el-buttonel-button typetext clickalert提示信息/el-buttonel-button typetext clicknotify通知信息/el-button/div
/templatescript
export default {name: kuang,methods: {// 单纯打开新页面openNotice() {this.$tab.openPage(打开的通知公告, /system/notice);},// 打开页面之后并且做一点其他事情。openNoticeAndWindows() {this.$tab.openPage(打开的通知公告并且弹窗, /system/notice).then(() {this.$modal.msg(我是弹窗)})},updateSelf() {//修改自己const obj Object.assign({}, this.$route, {title: 自定义标题})this.$tab.updatePage(obj);},updateOther() {// 修改别的标签页const obj Object.assign({}, {path: /system/notice}, {title: 自定义标题222})this.$tab.updatePage(obj).then(() {this.$modal.msg(修改页签完毕)})},// 提供成功、警告和错误等反馈信息msg() {this.$modal.msg(默认反馈);// this.$modal.msgError(错误反馈);// this.$modal.msgSuccess(成功反馈);// this.$modal.msgWarning(警告反馈);},// 提供成功、警告和错误等提示信息alert() {this.$modal.alert(默认提示);// this.$modal.alertError(错误提示);// this.$modal.alertSuccess(成功提示);// this.$modal.alertWarning(警告提示);},// 提供成功、警告和错误等通知信息notify() {this.$modal.notify(默认通知);// this.$modal.notifyError(错误通知);// this.$modal.notifySuccess(成功通知);// this.$modal.notifyWarning(警告通知);},}}
/scriptstyle/style增加一个确认窗口信息
confirm() {this.$modal.confirm(你确定要确定吗).then(function () {}).then(() {this.$modal.notify(点了确定)}).catch(() {this.$modal.notify(点了取消)});
},[!NOTE] 玩个好玩的开启遮罩层和关闭遮罩层 //遮罩层
startLoading() {// 打开遮罩层this.$modal.loading(正在Loading请稍后...);wait5Second().then(res {console.log(res)this.$modal.closeLoading();})
},
//如若已经loading则只能程序自己调用
stopLoading() {this.$modal.closeLoading();
}前端点击按钮开启遮罩层调用前端的wait5Second请求等待5s后关闭遮罩层。 去src/api/kuang/kuang.js下新增请求 import request from ../../utils/request;// 等5s
export function wait5Second() {return request({url: /wait5Second,headers: {isToken: false},method: get,})
} 在后端新增接口wait5Second 5.3、$auth
$auth对象用于验证用户是否拥有某些权限或角色它定义在plugins/auth.js文件中它有如下方法
验证用户权限
// 验证用户是否具备某权限
this.$auth.hasPermi(system:user:add);
// 验证用户是否含有指定权限只需包含其中一个
this.$auth.hasPermiOr([system:user:add, system:user:update]);
// 验证用户是否含有指定权限必须全部拥有
this.$auth.hasPermiAnd([system:user:add, system:user:update]);验证用户角色
// 验证用户是否具备某角色
this.$auth.hasRole(admin);
// 验证用户是否含有指定角色只需包含其中一个
this.$auth.hasRoleOr([admin, common]);
// 验证用户是否含有指定角色必须全部拥有
this.$auth.hasRoleAnd([admin, common]);我们来使用一下增加一个按钮,点击按钮判断用户是否具有system:user:list权限
templatedivh1验证权限/h1el-button typetext clickvalidMyPermission验证权限/el-button/div
/templatescriptexport default {name: kuang,methods: {//验证权限validMyPermission() {console.log(this.$auth.hasPermi(system:user:list));}}}
/scriptstyle/style我们看一下hasPermi的源码它调用了authPermission方法可以发现它是从Vuex的 store 中获取权限而Vuex的store中的 permission 又是后端传过来的 5.4、$download
$download对象用于文件下载它定义在plugins/download.js文件中。
对于图片用户上传头像若依的处理是将用户上传的头像存放在D:/ruoyi/upload目录下这个目录可以在yml中配置。 [!NOTE] 用户上传头像我们可以将头像的URI存在数据库中这样当使用用户头像或者下载用户头像的时候我们可以通过数据库中的URI进行下载。 5.4.1、根据名称下载upload路径下的文件 [!NOTE] 后期更新 6、Https实现
因为http自身是明码传输我们通过网卡抓包可以轻而易举得到token。甚至包括登录的账号密码所以要上https [!NOTE] 参考视频腾讯云实现HTTPS