bug修复:退出登录后再次登录不获取用户信息;增加锁屏页面;
This commit is contained in:
parent
939daa426c
commit
e116897b7e
@ -40,13 +40,19 @@ export default [
|
||||
method: 'post',
|
||||
timeout: 1000,
|
||||
statusCode: 200,
|
||||
response: {
|
||||
response: ({body}) => {
|
||||
// 响应内容
|
||||
return +body.password === 123456 ? {
|
||||
code: 200,
|
||||
message: '登录成功',
|
||||
data: {
|
||||
token: '@word(50, 100)', // @word()是mockjs的语法
|
||||
refresh_token: '@word(50, 100)', // refresh_token是用来重新生成token的
|
||||
},
|
||||
}
|
||||
} : {
|
||||
code: 400,
|
||||
message: '密码错误,请输入123456',
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
46
src/components/Avatar/index.vue
Normal file
46
src/components/Avatar/index.vue
Normal file
@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<div class="userinfo">
|
||||
<template v-if="!userinfo">
|
||||
<i class="el-icon-user" />
|
||||
<h3>admin</h3>
|
||||
</template>
|
||||
<template v-else>
|
||||
<img class="avatar" :src="userinfo.avatar" />
|
||||
<h3>{{ userinfo.name }}</h3>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from 'vue'
|
||||
import { useUserinfo } from './hooks/useUserinfo'
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const { userinfo } = useUserinfo()
|
||||
|
||||
return { userinfo }
|
||||
},
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.userinfo {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
i {
|
||||
font-size: 48px;
|
||||
color: $mainColor;
|
||||
}
|
||||
h3 {
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
margin: 8px 0;
|
||||
}
|
||||
.avatar {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -60,22 +60,14 @@
|
||||
custom-class="lock-modal"
|
||||
append-to-body
|
||||
>
|
||||
<div class="userinfo">
|
||||
<template v-if="!userinfo">
|
||||
<i class="el-icon-user" />
|
||||
<h3>admin</h3>
|
||||
</template>
|
||||
<template v-else>
|
||||
<img class="avatar" :src="userinfo.avatar" />
|
||||
<h3>{{ userinfo.name }}</h3>
|
||||
</template>
|
||||
</div>
|
||||
<Avatar />
|
||||
<el-form :model="lockModel" :rules="lockRules" ref="lockForm">
|
||||
<el-form-item label="锁屏密码" prop="password">
|
||||
<el-input
|
||||
type="password"
|
||||
v-model.trim="lockModel.password"
|
||||
autocomplete="off"
|
||||
@keyup.enter="submitForm"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
@ -94,16 +86,18 @@
|
||||
|
||||
<script>
|
||||
import { defineComponent, reactive, ref } from 'vue'
|
||||
import { useUserinfo } from './hooks/useUserinfo'
|
||||
import Avatar from '@/components/Avatar/index.vue'
|
||||
import { setItem } from '@/utils/storage'
|
||||
import { AesEncryption } from '@/utils/encrypt'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
Avatar,
|
||||
},
|
||||
setup() {
|
||||
const router = useRouter()
|
||||
const dialogVisible = ref(false)
|
||||
const { userinfo } = useUserinfo()
|
||||
const lockForm = ref(null)
|
||||
const lockModel = reactive({
|
||||
password: '',
|
||||
@ -123,13 +117,12 @@ export default defineComponent({
|
||||
// 存储到localStorage
|
||||
setItem('__VEA_SCREEN_LOCKED__', pwd)
|
||||
// 跳转到锁屏页面
|
||||
router.push('/lock')
|
||||
router.push('/lock?redirect=' + router.currentRoute.value.fullPath)
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
dialogVisible,
|
||||
userinfo,
|
||||
lockForm,
|
||||
lockModel,
|
||||
lockRules,
|
||||
@ -145,25 +138,6 @@ export default defineComponent({
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.userinfo {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
i {
|
||||
font-size: 48px;
|
||||
color: $mainColor;
|
||||
}
|
||||
h3 {
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
margin: 8px 0;
|
||||
}
|
||||
.avatar {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
.submit-btn {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@ -70,7 +70,7 @@
|
||||
import { defineComponent } from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useUserinfo } from './hooks/useUserinfo'
|
||||
import { useUserinfo } from '@/components/Avatar/hooks/useUserinfo'
|
||||
import LockModal from './LockModal.vue'
|
||||
|
||||
export default defineComponent({
|
||||
@ -86,9 +86,7 @@ export default defineComponent({
|
||||
// 退出
|
||||
const logout = () => {
|
||||
// 清除token
|
||||
store.commit('app/clearToken')
|
||||
// 清除用户信息
|
||||
store.commit('account/clearUserinfo')
|
||||
store.dispatch('app/clearToken')
|
||||
// 清除标签栏
|
||||
store.dispatch('tags/delAllTags')
|
||||
router.push('/login')
|
||||
|
||||
@ -1,45 +0,0 @@
|
||||
/*
|
||||
* ::
|
||||
* :;J7, :, ::;7:
|
||||
* ,ivYi, , ;LLLFS:
|
||||
* :iv7Yi :7ri;j5PL
|
||||
* ,:ivYLvr ,ivrrirrY2X,
|
||||
* :;r@Wwz.7r: :ivu@kexianli.
|
||||
* :iL7::,:::iiirii:ii;::::,,irvF7rvvLujL7ur
|
||||
* ri::,:,::i:iiiiiii:i:irrv177JX7rYXqZEkvv17
|
||||
* ;i:, , ::::iirrririi:i:::iiir2XXvii;L8OGJr71i
|
||||
* :,, ,,: ,::ir@mingyi.irii:i:::j1jri7ZBOS7ivv,
|
||||
* ,::, ::rv77iiiriii:iii:i::,rvLq@huhao.Li
|
||||
* ,, ,, ,:ir7ir::,:::i;ir:::i:i::rSGGYri712:
|
||||
* ::: ,v7r:: ::rrv77:, ,, ,:i7rrii:::::, ir7ri7Lri
|
||||
* , 2OBBOi,iiir;r:: ,irriiii::,, ,iv7Luur:
|
||||
* ,, i78MBBi,:,:::,:, :7FSL: ,iriii:::i::,,:rLqXv::
|
||||
* : iuMMP: :,:::,:ii;2GY7OBB0viiii:i:iii:i:::iJqL;::
|
||||
* , ::::i ,,,,, ::LuBBu BBBBBErii:i:i:i:i:i:i:r77ii
|
||||
* , : , ,,:::rruBZ1MBBqi, :,,,:::,::::::iiriri:
|
||||
* , ,,,,::::i: @arqiao. ,:,, ,:::ii;i7:
|
||||
* :, rjujLYLi ,,:::::,:::::::::,, ,:i,:,,,,,::i:iii
|
||||
* :: BBBBBBBBB0, ,,::: , ,:::::: , ,,,, ,,:::::::
|
||||
* i, , ,8BMMBBBBBBi ,,:,, ,,, , , , , , :,::ii::i::
|
||||
* : iZMOMOMBBM2::::::::::,,,, ,,,,,,:,,,::::i:irr:i:::,
|
||||
* i ,,:;u0MBMOG1L:::i:::::: ,,,::, ,,, ::::::i:i:iirii:i:i:
|
||||
* : ,iuUuuXUkFu7i:iii:i:::, :,:,: ::::::::i:i:::::iirr7iiri::
|
||||
* : :rk@Yizero.i:::::, ,:ii:::::::i:::::i::,::::iirrriiiri::,
|
||||
* : 5BMBBBBBBSr:,::rv2kuii:::iii::,:i:,, , ,,:,:i@petermu.,
|
||||
* , :r50EZ8MBBBBGOBBBZP7::::i::,:::::,: :,:,::i;rrririiii::
|
||||
* :jujYY7LS0ujJL7r::,::i::,::::::::::::::iirirrrrrrr:ii:
|
||||
* ,: :@kevensun.:,:,,,::::i:i:::::,,::::::iir;ii;7v77;ii;i,
|
||||
* ,,, ,,:,::::::i:iiiii:i::::,, ::::iiiir@xingjief.r;7:i,
|
||||
* , , ,,,:,,::::::::iiiiiiiiii:,:,:::::::::iiir;ri7vL77rrirri::
|
||||
* :,, , ::::::::i:::i:::i:i::,,,,,:,::i:i:::iir;@Secbone.ii:::
|
||||
*
|
||||
* @Descripttion:
|
||||
* @version:
|
||||
* @Date: 2021-04-23 14:08:17
|
||||
* @LastEditors: huzhushan@126.com
|
||||
* @LastEditTime: 2021-04-23 14:08:18
|
||||
* @Author: huzhushan@126.com
|
||||
* @HomePage: https://huzhushan.gitee.io/vue3-element-admin
|
||||
* @Github: https://github.com/huzhushan/vue3-element-admin
|
||||
* @Donate: https://huzhushan.gitee.io/vue3-element-admin/donate/
|
||||
*/
|
||||
@ -47,7 +47,7 @@ const getPageTitle = title => {
|
||||
}
|
||||
|
||||
// 白名单,里面是路由对象的name
|
||||
const WhiteList = ['login', 'forbidden', 'server-error', 'not-found']
|
||||
const WhiteList = ['login', 'forbidden', 'server-error', 'not-found', 'lock']
|
||||
|
||||
// vue-router4的路由守卫不再是通过next放行,而是通过return返回true或false或者一个路由地址
|
||||
router.beforeEach(async to => {
|
||||
@ -65,11 +65,6 @@ router.beforeEach(async to => {
|
||||
replace: true,
|
||||
}
|
||||
} else {
|
||||
// 判断是否处于锁屏状态
|
||||
if (to.name !== 'lock' && !!getItem('__VEA_SCREEN_LOCKED__')) {
|
||||
return { name: 'lock', replace: true }
|
||||
}
|
||||
|
||||
// 获取用户角色信息,根据角色判断权限
|
||||
let userinfo = store.state.account.userinfo
|
||||
if (!userinfo) {
|
||||
@ -80,6 +75,18 @@ router.beforeEach(async to => {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 判断是否处于锁屏状态
|
||||
if (to.name !== 'lock' && !!getItem('__VEA_SCREEN_LOCKED__')) {
|
||||
return {
|
||||
name: 'lock',
|
||||
query: {
|
||||
redirect: to.path,
|
||||
},
|
||||
replace: true,
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有权限,跳转到403页面
|
||||
if (
|
||||
!!to.meta &&
|
||||
|
||||
@ -48,5 +48,12 @@ export default {
|
||||
state.device = device
|
||||
},
|
||||
},
|
||||
actions: {},
|
||||
actions: {
|
||||
clearToken({ commit }) {
|
||||
// 清除token
|
||||
commit('clearToken')
|
||||
// 清除用户信息
|
||||
commit('account/clearUserinfo', '', { root: true })
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@ -69,7 +69,8 @@ service.interceptors.response.use(
|
||||
// 校验是否有 refresh_token
|
||||
const { authorization } = store.state.app
|
||||
if (!authorization || !authorization.refresh_token) {
|
||||
router.push('/login')
|
||||
const redirect = encodeURIComponent(window.location.href)
|
||||
router.push(`/login?redirect=${redirect}`)
|
||||
|
||||
// 代码不要往后执行了
|
||||
return Promise.reject(error)
|
||||
@ -97,9 +98,10 @@ service.interceptors.response.use(
|
||||
} catch (err) {
|
||||
// 如果获取失败,直接跳转 登录页
|
||||
// console.log('请求刷新 token 失败', err)
|
||||
router.push('/login')
|
||||
const redirect = encodeURIComponent(window.location.href)
|
||||
router.push(`/login?redirect=${redirect}`)
|
||||
// 清除token
|
||||
store.commit('app/clearToken')
|
||||
store.dispatch('app/clearToken')
|
||||
return Promise.reject(error)
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,9 +47,144 @@
|
||||
<template>
|
||||
<h1 class="title">
|
||||
⚡屏幕已锁定
|
||||
<div class="unlock"><i class="el-icon-lock"></i>解锁</div>
|
||||
<div class="unlock-btn" @click="handleUnlock">
|
||||
<i class="el-icon-unlock"></i>解锁
|
||||
</div>
|
||||
</h1>
|
||||
<div class="unlock-modal" v-show="showModal">
|
||||
<Avatar class="userinfo-unlock" />
|
||||
<el-form :model="lockModel" :rules="lockRules" ref="lockForm">
|
||||
<el-form-item prop="password">
|
||||
<el-input
|
||||
type="password"
|
||||
v-model.trim="lockModel.password"
|
||||
autocomplete="off"
|
||||
placeholder="请输入锁屏密码或登录密码"
|
||||
@keyup.enter="submitForm"
|
||||
>
|
||||
<template #append>
|
||||
<el-button
|
||||
type="primary"
|
||||
class="btn-unlock"
|
||||
icon="el-icon-right"
|
||||
@click="submitForm"
|
||||
></el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="cancel" type="text">取消</el-button>
|
||||
<el-button @click="reLogin" type="text">重新登录</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { defineComponent, ref, reactive, getCurrentInstance } from 'vue'
|
||||
import Avatar from '@/components/Avatar/index.vue'
|
||||
import { getItem, removeItem } from '@/utils/storage'
|
||||
import { AesEncryption } from '@/utils/encrypt'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useStore } from 'vuex'
|
||||
import { Login } from '@/api/login'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
Avatar,
|
||||
},
|
||||
setup() {
|
||||
const { ctx } = getCurrentInstance()
|
||||
const store = useStore()
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const showModal = ref(false)
|
||||
const lockForm = ref(null)
|
||||
const lockModel = reactive({
|
||||
password: '',
|
||||
})
|
||||
|
||||
const checkPwd = async (rule, value, callback) => {
|
||||
const encryption = new AesEncryption()
|
||||
const cipher = getItem('__VEA_SCREEN_LOCKED__')
|
||||
if (!cipher) {
|
||||
return callback()
|
||||
}
|
||||
const pwd = encryption.decryptByAES(cipher)
|
||||
if (pwd === value) {
|
||||
return callback()
|
||||
} else {
|
||||
// 尝试登录
|
||||
const { code } = await Login({
|
||||
username: store.state.account.userinfo.name,
|
||||
password: value,
|
||||
})
|
||||
|
||||
if (+code === 200) {
|
||||
return callback()
|
||||
}
|
||||
}
|
||||
return callback(new Error(rule.message))
|
||||
}
|
||||
|
||||
const lockRules = reactive({
|
||||
password: [
|
||||
{ required: true, message: '请输入锁屏密码' },
|
||||
{
|
||||
validator: checkPwd,
|
||||
message: '密码错误',
|
||||
trigger: 'none',
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const handleUnlock = () => {
|
||||
// 判断当前是否登录
|
||||
const { authorization } = store.state.app
|
||||
if (authorization) {
|
||||
showModal.value = true
|
||||
// 尝试获取用户信息
|
||||
!store.state.account.userinfo && store.dispatch('account/getUserinfo')
|
||||
} else {
|
||||
ctx.$message('您的账号已退出,请直接登录')
|
||||
reLogin()
|
||||
}
|
||||
}
|
||||
|
||||
const submitForm = () => {
|
||||
lockForm.value.validate(async valid => {
|
||||
if (!valid) {
|
||||
return false
|
||||
}
|
||||
|
||||
// 返回锁屏前的页面
|
||||
removeItem('__VEA_SCREEN_LOCKED__')
|
||||
router.push({ path: route.query.redirect || '/', replace: true })
|
||||
})
|
||||
}
|
||||
|
||||
const cancel = () => {
|
||||
lockForm.value.resetFields()
|
||||
showModal.value = false
|
||||
}
|
||||
|
||||
const reLogin = () => {
|
||||
removeItem('__VEA_SCREEN_LOCKED__')
|
||||
router.push('/login?redirect=' + (route.query.redirect || '/'))
|
||||
}
|
||||
|
||||
return {
|
||||
showModal,
|
||||
lockForm,
|
||||
lockModel,
|
||||
lockRules,
|
||||
handleUnlock,
|
||||
submitForm,
|
||||
cancel,
|
||||
reLogin,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.title {
|
||||
@ -59,7 +194,7 @@
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
.unlock {
|
||||
.unlock-btn {
|
||||
color: #aaa;
|
||||
font-size: 16px;
|
||||
font-weight: normal;
|
||||
@ -67,4 +202,37 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.unlock-modal {
|
||||
position: fixed;
|
||||
z-index: 10;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
backdrop-filter: blur(8px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
::v-deep(.el-input-group__append) {
|
||||
background: $mainColor !important;
|
||||
border-color: $mainColor !important;
|
||||
}
|
||||
|
||||
.btn-unlock {
|
||||
background: $mainColor !important;
|
||||
color: #fff !important;
|
||||
::v-deep(i) {
|
||||
transform: scale(1.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
.userinfo-unlock {
|
||||
margin-bottom: 16px;
|
||||
::v-deep(h3) {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -58,6 +58,7 @@ import {
|
||||
import { Login } from '@/api/login'
|
||||
import { useStore } from 'vuex'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'login',
|
||||
setup() {
|
||||
@ -100,8 +101,17 @@ export default defineComponent({
|
||||
message: '登录成功',
|
||||
duration: 1000,
|
||||
})
|
||||
const targetPath = route.query.redirect
|
||||
router.push(targetPath ? targetPath : '/')
|
||||
|
||||
const targetPath = decodeURIComponent(route.query.redirect)
|
||||
if (targetPath.startsWith('http')) {
|
||||
// 如果是一个url地址
|
||||
window.location.href = targetPath
|
||||
} else if (targetPath.startsWith('/')) {
|
||||
// 如果是内部路由地址
|
||||
router.push(targetPath)
|
||||
} else {
|
||||
router.push('/')
|
||||
}
|
||||
store.commit('app/setToken', data)
|
||||
} else {
|
||||
ctx.$message.error(message)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user