vue3实现动态路由配置

思路:

1、登录成功后,返回动态的路由信息,存储在本地localStorage中。

2、router.js中在路由守卫钩子函数中使用router.addRoute()进行动态路由添加。

具体代码:

 

login.vue中

<script setup>
import {onMounted, reactive, ref} from 'vue';
import {KeyOutlined, SafetyOutlined, UserOutlined} from '@ant-design/icons-vue';
import sha1 from 'js-sha1';
import {useRoute, useRouter} from 'vue-router';
import appConstant from '../app-constant.js';
import commonService from '../service/common-service.js';
import sysUserService from '../service/sys-user-service.js';

const router = useRouter();
const route = useRoute();

const logoutAlert = ref(route.query.hasOwnProperty('logout'));
const closeLogoutAlert = () => {
  logoutAlert.value = false;
};

const formState = reactive({
  account: '',
  password: '',
  captcha: '',
});

const alertError = reactive({show: false, msg: ''});
const login = (data) => {
  loginLoading.value = true;
//sessionDtorage存储接口返回的动态路由地址 sysUserService.login(data).then(res
=> { sessionStorage.setItem(appConstant.SK_LOGIN_USER, JSON.stringify(res.data)); router.replace('/home'); }).catch(error => { alertError.show = true; alertError.msg = error.msg; }).finally(() => { getCaptcha(); loginLoading.value = false; }); }; const loginLoading = ref(false); const onFormFinish = (values) => { let password = ''; if (values.password) { password = sha1(sha1(values.password)); } const data = { account: values.account, password, captcha: values.captcha }; login(data); }; const captchaImgSrc = ref(null); const getCaptcha = () => { commonService.getCaptcha().then(res => { captchaImgSrc.value = res; }); }; onMounted(() => { getCaptcha(); }); const onCaptchaClick = () => { getCaptcha(); }; </script> <template> <div class="login"> <main> <section class="left"> <img src="../assets/yiyuan-logo.jpg" alt="logo"> <div class="title">管理系统</div> <div class="sub-title">System</div> </section> <section class="right"> <div> <span class="title">用户登录</span> <span class="sub-title">USER LOGIN</span> </div> <a-form :model="formState" @finish="onFormFinish"> <a-form-item name="account" :rules="[{ required: true, message: '用户名不能为空' }]"> <a-input v-model:value="formState.account" placeholder="请输入用户名" class="input" @click="closeLogoutAlert" > <template #prefix> <user-outlined class="input-icon"/> </template> </a-input> </a-form-item> <a-form-item name="password" :rules="[{ required: true, message: '密码不能为空' }]"> <a-input-password v-model:value="formState.password" placeholder="请输入登录密码" class="input" @click="closeLogoutAlert" > <template #prefix> <key-outlined class="input-icon"/> </template> </a-input-password> </a-form-item> <a-form-item name="captcha" :rules="[{ required: true, message: '校验码不能为空' }]"> <a-row> <a-col :span="16"> <a-input v-model:value="formState.captcha" placeholder="请输入校验码" class="input" style="width: 270px" @click="closeLogoutAlert" > <template #prefix> <safety-outlined class="input-icon"/> </template> </a-input> </a-col> <a-col :span="8" style="display: flex; justify-content: flex-end;"> <img class="captcha-img" :src="captchaImgSrc" alt="校验码" @click="onCaptchaClick"> </a-col> </a-row> </a-form-item> <a-form-item> <a-button type="primary" html-type="submit" block :loading="loginLoading" class="input" style="font-size: 18px;font-weight: bolder;" > 登录 </a-button> </a-form-item> </a-form> <!--错误反馈--> <a-alert v-if="alertError.show" :message="alertError.msg" type="error" show-icon style="border-radius: 6px;" /> <!--退出登录反馈--> <a-alert v-if="logoutAlert" message="您已成功退出" type="success" show-icon style="border-radius: 6px;" /> </section> </main> </div> </template> <style scoped lang="less"> @import "../assets/var"; .login { min-height: 100vh; display: flex; justify-content: center; align-items: center; background-color: #ecf5ff; main { width: 800px; height: 540px; background-color: white; border-radius: 8px; box-shadow: @box-shadow-base; display: flex; .left { width: 330px; background-image: url(../assets/login-bg-2.svg), url(../assets/login-bg-1.svg); background-position: left top, left top; background-repeat: no-repeat no-repeat; display: flex; flex-direction: column; align-items: center; color: white; border-top-left-radius: 8px; border-bottom-left-radius: 8px; .title { font-size: 20px; font-weight: bolder; } .sub-title { font-size: 12px; margin-top: 8px; } img { width: 80px; border-radius: 50%; margin: 32px 0 24px 0; } } .right { flex-grow: 1; padding: 32px; .title { font-size: 18px; color: #666; font-weight: bolder; display: inline-block; margin-right: 8px; } .sub-title { font-size: 14px; color: #999; } .ant-form { margin-top: 80px; .input { height: 50px; border-radius: 6px; .input-icon { font-size: 24px; color: #cccccc; } &.ant-input-affix-wrapper-focused { .input-icon { color: #409eff; } } } .captcha-img { width: 126px; height: 49px; border: 1px solid #d9d9d9; border-radius: 6px; cursor: pointer; } } } } } </style>

 

 

router.js中

import {createRouter, createWebHistory} from 'vue-router';
import routes from './routes.js';
import NProgress from 'nprogress';
import 'nprogress/nprogress.css';
import {getSessionStorage, isBlank, isNil} from './util/common-util.js';
import appConstant from './app-constant.js';
import {Modal} from 'ant-design-vue';
// modules全局使用,以免上生产后页面无法访问
const modules = import.meta.glob('./pages/**/*.vue');

const router = createRouter({
  history: createWebHistory(),
  routes
});

router.beforeEach(async (to, from) => {

  Modal.destroyAll();
  NProgress.start();

  if (to.path !== '/') {
    if (!hasNecessaryRoute(to)) {
      const routeObj = generateRoute(to);
      if (isNil(routeObj)) {
        return '/404'
      } else {
        router.addRoute('Layout', routeObj);
        return to.fullPath;
      }
    }
  }

  const loginUser = getSessionStorage(appConstant.SK_LOGIN_USER, false);
  const isLogin = !isBlank(loginUser);

  if (!isLogin) {
    // 未登录,只允许访问登录页面
    if (to.path !== '/login') {
      return '/login';
    }

  } else {
    // 已登录,不允许访问登录页面
    if (to.path === '/login') {
      return from.path;
    }
  }

  if (to.path === '/') {
    // 不同登录状态下,对 / 路径的重定向
    return isLogin ? '/home' : '/login';
  }

  return true;

});

router.afterEach(() => {
  NProgress.done();
});
// 判断当前跳转的路由是否在router.getRoutes()里面
function hasNecessaryRoute(to) {
  return router.getRoutes().findIndex(r => r.path === to.path) !== -1;
}

function generateRoute(to) {
  const menus = getSessionStorage(appConstant.SK_LOGIN_USER, true)?.menus;
  if (isBlank(menus)) {
    return null;
  }

  const find = menus.filter(m => m.menuType === 'MENU').find(m=>m.linkLocation === to.path);
  if (isNil(find)) {
    return null;
  }

  return {
    path: find.linkLocation,
    component: modules[`./${find.componentLocation}`]
  };
}

export default router;

 

posted @ 2023-02-16 14:25  Evident  阅读(1717)  评论(0编辑  收藏  举报