效果
无token情况下 访问dashboard
刷新跳转到登录页,url上redirect携带的是之前访问过的路径 之前query也未丢失
登录成功后
登录成功后 重定向回 dashboard url参数依然存在 (url参数都是 useRouteQuery hook功劳 在下面有实现)
4-1 安装nProgress进度条
npm install --save nprogress# tsnpm install -D @types/nprogress
4-2 添加路由验证
创建permission.ts
添加路由导航钩子,进行登录验证
src/permission.ts
import router from '@/router'import nProgress from 'nprogress'import 'nprogress/nprogress.css' // progress bar styleimport { getToken } from './utils/auth'nProgress.configure({ showSpinner: false })const whiteList = ['/login'] // 白名单router.beforeEach((to) => {nProgress.start()const hasToken = getToken()if (hasToken) { // 有token代表已登录if (to.path === '/login') {nProgress.done()return {path: '/',replace: true}}nProgress.done()return true} else {if (whiteList.includes(to.path)) {nProgress.done()return true}nProgress.done()return {path: '/login',query: {redirect: to.path,...to.query}}}})router.afterEach(() => {nProgress.done()})
4-3 登录后跳转逻辑修改

src/views/login/index.vue
<template><div class="login-container"><el-formclass="login-form":model="loginForm":rules="loginRules"ref="loginFormRef"><div class="admin-logo"><img class="logo" src="../../assets/logo.png" alt="logo"><h1 class="name">Vue3 Admin</h1></div><el-form-item prop="username"><span class="svg-container"><svg-icon icon-class="user"></svg-icon></span><el-inputref="usernameRef"placeholder="请输入用户名"v-model="loginForm.username"autocomplete="off"tabindex="1"/></el-form-item><el-form-item prop="password"><span class="svg-container"><svg-icon icon-class="password"></svg-icon></span><el-inputref="passwordRef":class="{'no-autofill-pwd': passwordType === 'password'}"placeholder="请输入密码"v-model="loginForm.password"type="text"autocomplete="off"tabindex="2"/><span class="show-pwd" @click="showPwd"><svg-icon:icon-class="passwordType === 'password' ? 'eye' : 'eye-open'" /></span></el-form-item><!-- 登录按钮 --><el-buttontype="primary"style=" width: 100%; margin-bottom: 30px":loading="loading"@click="handleLogin">Login</el-button></el-form></div></template><script lang="ts">import { defineComponent, ref, reactive, toRefs, onMounted } from 'vue'import { ElForm } from 'element-plus'import { useRouter } from 'vue-router'import { useStore } from '@/store'import useRouteQuery from './hooks/useRouteQuery'type IElFormInstance = InstanceType<typeof ElForm>export default defineComponent({name: 'Login',setup() {const store = useStore()const router = useRouter()const loading = ref(false) // 登录加载状态// form refconst loginFormRef = ref<IElFormInstance | null>(null)// form username refconst usernameRef = ref<HTMLInputElement | null>(null)// form password refconst passwordRef = ref<HTMLInputElement | null>(null)const loginState = reactive({loginForm: {username: '',password: ''},loginRules: {username: [{required: true,trigger: 'blur',message: '请输入用户名!'}],password: [{required: true,trigger: 'blur',message: '请输入密码!'}]},passwordType: 'password'})// 显示密码const showPwd = () => {loginState.passwordType = loginState.passwordType === 'password' ? 'text' : 'password'}// 重定向router query处理const { redirect, otherQuery } = useRouteQuery()// 登录const handleLogin = () => {(loginFormRef.value as IElFormInstance).validate((valid) => {if (valid) {loading.value = truestore.dispatch('user/login', loginState.loginForm).then(() => {// 登录成功后跳转之前被访问页或首页router.push({path: redirect.value || '/',query: otherQuery.value})}).finally(() => {loading.value = false})} else {console.log('error submit!!')}})}// 自动获取焦点onMounted(() => {if (loginState.loginForm.username === '') {(usernameRef.value as HTMLInputElement).focus()} else if (loginState.loginForm.password === '') {(passwordRef.value as HTMLInputElement).focus()}})return {loading,loginFormRef,handleLogin,showPwd,usernameRef,passwordRef,...toRefs(loginState)}}})</script><style lang="scss">$bg:#283443;$light_gray:#fff;$cursor: #fff;.login-container {.el-form-item {border: 1px solid #dcdee2;border-radius: 5px;.el-input {display: inline-block;height: 40px;width: 85%;input {background: transparent;border: 0;-webkit-appearance: none;border-radius: 0px;padding: 12px 5px 12px 15px;height: 40px;}}}.no-autofill-pwd { // 解决自动填充问题.el-input__inner { // 模仿密码框原点-webkit-text-security: disc !important;}}}</style><style lang="scss" scoped>$bg:#2d3a4b;$dark_gray:#889aa4;$light_gray:#eee;.login-container {min-height: 100%;width: 100%;overflow: hidden;background-image: url('../../assets/body.svg');background-repeat: no-repeat;background-position: 50%;background-size: 100%;.login-form {position: relative;width: 500px;max-width: 100%;margin: 0 auto;padding: 140px 35px 0;overflow: hidden;box-sizing: border-box;.svg-container {padding: 0 10px;}.show-pwd {font-size: 16px;cursor: pointer;margin-left: 7px;}.admin-logo {display: flex;align-items: center;justify-content: center;margin-bottom: 20px;.logo {width: 60px;height: 60px;}.name {font-weight: normal;margin-left: 10px;}}}}</style>
创建useRouteQuery hook
获取url query 并得到redirect参数 redirec参数或作为登录成功后跳转路径
src/views/login/hooks/useRouteQuery.ts
import { ref, Ref, watchEffect } from 'vue'import { useRoute, LocationQueryRaw } from 'vue-router'interface RouteQuery {redirect: Ref<string>;otherQuery: Ref<LocationQueryRaw | undefined>;}const useRouteQuery = (): RouteQuery => {const route = useRoute()const query = route.queryconst redirect = ref('')const otherQuery = ref<LocationQueryRaw | undefined>(undefined)const getOtherQuery = (query: LocationQueryRaw) => {return Object.keys(query || {}).filter(q => q !== 'redirect').reduce((obj, key) => {obj[key] = query[key]return obj}, {} as LocationQueryRaw)}otherQuery.value = getOtherQuery(query)// 不使用watch(route) 原因说明:// 尤大回应 https://www.gitmemory.com/issue/vuejs/vue-next/2027/685247838// https://blog.csdn.net/weixin_47339511/article/details/117221559// 修复使用watch 监听route 性能开销问题watchEffect(() => {const query = route.queryif (query) {redirect.value = query.redirect as stringotherQuery.value = getOtherQuery(query as LocationQueryRaw)}})return {redirect,otherQuery}}export default useRouteQuery
本节参考源码
https://gitee.com/brolly/vue3-element-admin/commit/a4471f25ee5d88125dc7ee3569b2122beb384a07
