相同浏览器只允许登录一个账号的需求相信是很多前端小伙伴的面临过或者将要面临的问题,看似没有什么大问题的问题,其实有点东西的。这里介绍一下我尝试多种办法之后得出的一个最优的解决办法
情况一
当窗口一登录了 admin 账号,再去复制项目里面的地址用窗口二打开
注意这里是项目里面的地址,有点东西的地方在于登录地址,下面再介绍,且看:窗口二打开任意一个项目里面的地址我们要的效果都是可以直接访问的(各大 bat 的产品也是这样的效果,毋庸置疑)。那么我们是怎么做到当窗口一登录了之后,其他窗口可以访问项目里面的任意地址,而当没有窗口登录,复制项目里面的地址不能访问(安全问题)让其跳转到登录页
上代码:我们在 main.js 里面
router.beforeEach((to,from,next) => { //这里是在所有路由跳转之前都会去执行以下操作
if(to.path == '/'){ //如果要去的是登录页
next() //那么随意,去就是了
} else { //如果不是
if(localStorage.getItem('isLogin')){ //判断当前浏览器的本地存储里面是不是有已经登录的信息
next() //有的话,ok,去你想去的地址
} else { //没有的话,也就是没有登录
next({path:'/'}) //去登录页
}
}
})
这里的 isLogin 是我自己命名在 login.vue 的登录的时候存进去本地的,当然也可以存接口返回的 user 信息,whatever you like
// 登录按钮执行登录的时候,至于为什么存localStorage,不用sessionStore,自行百度其中的区别,相信你会明了的
localStorage.setItem("isLogin", 1)
这里要注意,在退出按钮的时候要执行清除登录状态的操作,否则本地存储会一直保留(不手动清缓存的情况下)
// 执行退出登录操作的时候执行
localStorage.removeItem("isLogin")
到这里应该都没有什么问题,都是常规操作 >o<!
情况二
当窗口二去到登录页面,并且登录了另一个非 admin 的账号
这种情况下,我们是不是要把窗口一的 admin 的账号覆盖掉?是的,可能菜单权限不一样,页面风格都不一样的情况下,我们怎么去覆盖这个页面所展示的信息? 刚开始我的想法是监听浏览器的窗口切换,再次切回窗口一的时候再次点击页面上的某处去执行接口请求的时候刷新页面,这是有逻辑上的问题的,因为我们并不能检测到页面是否有接口请求,只能接测到是否有路由跳转,但是页面上只有一部分比如菜单点击才有路由跳转,所以这个方法是有问题的。(这段废话是为了更好的表明我的思路 emmmm......)
换种思路!要实现这个逻辑,我们就必须要检测到浏览器是否有新窗口执行了账号登录,也就是监测浏览器的本地存储是否有东西,vue的watch监测并不能监测非vue机制存放的数据
(这句话我在上一篇文章中也提过),也就是说 watch 不能实时监测 localStorage 里面的数据,而我们或许会想到把 localStorage 里面的数据在保存到 vuex 里面去达到实时监测的效果,但是,vuex 会在其他窗口登录的时候被重置,但是这个思路是对的!!!
直接说我怎么实现的:手动去创建一个 js 文佳再在里面创建一个全局变量 export 出去,把这个全局变量创建在一个单独的文件中,然后再去组件里面引用,这个全局变量是能被 watch 监测
要做到这个全局变量不被其他窗口的登录给重置掉,这里就是个小心思了在登陆组件 login.vue 里面引入这个全局变量,在登陆按钮执行成功的时候去赋值,主要看带注释的地方
// login.vue先引入
import global from '@/config/common.js'
methods: {
login () {
if (this.user == '') {
this.$message.error('账号不能为空!');
} else if (this.password == '') {
this.$message.error('密码不能为空!');
} else {
this.setCookie(this.user)
this.$post('/api/manage/user/login.json', {
userName: this.user,
passWord: this.$md5(this.password)
}).then(res => {
localStorage.setItem("isLogin", 1)
global.aaa = res.obj.id //这里才是登录成功的时候去赋值,我是创建的 aaa 这个变量为例子
localStorage.setItem('userId', res.obj.id) // 同时也要做的是将这个用户id存进localStorage
this.$router.push({path: '/container'})
})
.catch(err => {
this.$message.error(err.msg)
})
}
}
}
那我们登录成功之后会在本地代码里面保存这个用户 id,也在浏览器的 localStorage 里面保存了一份,接着,在 main.js 里面
import global from '@/config/common.js'
window.addEventListener("visibilitychange",function(){ //这个方法是监测浏览器窗口发生变化的时候执行
if (document.hidden == false && global.aaa != localStorage.getItem('userId')) {
global.aaa = localStorage.getItem('userId') //只有当初始创建的aaa不等于localStorage里面的userId的时候去覆盖掉这个aaa
}
//不覆盖的话aaa永远都是我们设的初始值
});
最后,我们在 app.vue 里面去监听这个全局变量的变化
watch: {
'global.aaa' : 'refresh'
},
methods: {
refresh () {
//执行刷新操作,这里我没有写this.$route.go(0),因为整个页面抖动不是很友好
//现将app.vue下面的 头部组件,菜单组件,内容组件隐藏再放出来,实现刷新一样的效果
this.hackReset = false
this.$nextTick(() => {
this.hackReset = true
})
}
}
好了,在第二个窗口登录另一个账号,返回第一个窗口的时候,改窗口登了的账号被挤掉,整个页面同步到第二个窗口所登录的账号信息,这个效果实现了!!!代码倒是没有什么很高深的要写,只是思路很重要,有点曲折,实现完功能发现,头发又掉了好几根。
结束语
本文提供作者:蜗牛先笙
觉得文章写的好,请给戳↑链接关注作者,给文章点个赞嗷~