【Vue3-Element-Admin 动态路由】涉及到的配置

Vue3-Element-Admin 动态路由 涉及到的配置

  • 0. Vue3-Element-Admin 项目地址
  • 1. router/index.ts
  • 2. Mock接口模拟数据
  • 3. store/permission
  • 4. api/menu
  • 5. plugins/permission

这篇文章讲的主要是 Vue3-Element-Admin 差不多内置的动态路由配置 (根据后端接口渲染)

先把开发环境(.env.development)中的 VITE_MOCK_DEV_SERVER 设置为 true 这代表启用 Mock 服务
Mock 数据模拟 在 vite 中已经配置好了
在这里插入图片描述

0. Vue3-Element-Admin 项目地址

Vue3-Element-Admin:Vue3-Element-Admin

1. router/index.ts

这个文件主要放一些静态初始路由,可以不用管

import type { App } from 'vue'
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'

export const Layout = () => import('@/layout/index.vue')

// 静态路由
export const constantRoutes: RouteRecordRaw[] = [
  {
    path: '/redirect',
    component: Layout,
    meta: { hidden: true },
    children: [
      {
        path: '/redirect/:path(.*)',
        component: () => import('@/views/redirect/index.vue')
      }
    ]
  },

  {
    path: '/login',
    component: () => import('@/views/login/index.vue'),
    meta: { hidden: true }
  },

  {
    path: '/',
    name: '/',
    component: Layout,
    redirect: '/dashboard',
    children: [
      {
        path: 'dashboard',
        component: () => import('@/views/dashboard/index.vue'),
        name: 'Dashboard', // 用于 keep-alive, 必须与SFC自动推导或者显示声明的组件name一致
        // https://cn.vuejs.org/guide/built-ins/keep-alive.html#include-exclude
        meta: {
          title: 'dashboard',
          icon: 'homepage',
          affix: true,
          keepAlive: true,
          alwaysShow: false
        }
      },
      {
        path: '401',
        component: () => import('@/views/error-page/401.vue'),
        meta: { hidden: true }
      },
      {
        path: '404',
        component: () => import('@/views/error-page/404.vue'),
        meta: { hidden: true }
      }
    ]
  }
]

/**
 * 创建路由
 */
const router = createRouter({
  history: createWebHistory(),
  routes: constantRoutes,
  // 刷新时,滚动条位置还原
  scrollBehavior: () => ({ left: 0, top: 0 })
})

// 全局注册 router
export function setupRouter(app: App<Element>) {
  app.use(router)
}

/**
 * 重置路由
 */
export function resetRouter() {
  router.replace({ path: '/login' })
}

export default router

2. Mock接口模拟数据

如果目前没有后端接口支持的话,可以先去文件根目录 mock 文件夹中的 menu.mock.ts 查看,一开始可能会有很多数据,全改成我的就好,可以先模拟看一下

import { defineMock } from './base'

export default defineMock([
  {
    url: 'menus/routes',
    method: ['GET'],
    body: {
      code: '00000',
      data: [
        {
          path: '/dashboard',
          component: 'Dashboard',
          redirect: '/dashboard',
          name: '/dashboard',
          meta: {
            title: '首页',
            icon: 'dashboard',
            hidden: true,
            roles: ['ADMIN'],
            alwaysShow: false,
            params: null
          }
        },
        {
          path: '/nihao',
          component: 'Layout',
          redirect: '/nihao/hello',
          name: '/nihao',
          meta: {
            title: '你好',
            icon: 'system',
            hidden: false,
            roles: ['ADMIN'],
            alwaysShow: true,
            params: null
          },
          children: [
            {
              path: 'hello',
              component: 'nihao/hello/index',
              name: 'Hello',
              meta: {
                title: 'Hello',
                icon: 'user',
                hidden: false,
                roles: ['ADMIN'],
                keepAlive: true,
                alwaysShow: false,
                params: null
              }
            }
          ]
        },
        {
          path: '/system',
          component: 'Layout',
          redirect: '/system/user',
          name: '/system',
          meta: {
            title: '系统管理',
            icon: 'system',
            hidden: false,
            roles: ['ADMIN'],
            alwaysShow: true,
            params: null
          },
          children: [
            {
              path: 'user',
              component: 'system/user/index',
              name: 'User',
              meta: {
                title: 'Test1',
                icon: 'user',
                hidden: false,
                roles: ['ADMIN'],
                keepAlive: true,
                alwaysShow: false,
                params: null
              }
            },
            {
              path: 'user',
              component: 'system/user/index',
              name: 'User',
              meta: {
                title: 'Test2',
                icon: 'user',
                hidden: false,
                roles: ['ADMIN'],
                keepAlive: true,
                alwaysShow: false,
                params: null
              }
            }
          ]
        }
      ],
      msg: '一切ok'
    }
  }
  // ... 其他接口
])

3. store/permission

查看权限配置相关页面,这边主要是做一些角色鉴权、角色菜单权限,差不多都是菜单相关配置,主要看一下 generateRoutes 方法,它是配置动态路由所需要用到的方法,这边我是使用了我上面那个 Mock 接口,你可以看到
MenuAPI.getRoutes() .then(data => { //... })

import { RouteRecordRaw } from 'vue-router'
import { constantRoutes } from '@/router'
import { store } from '@/store'
import MenuAPI from '@/api/menu'
import { RouteVO } from '@/api/menu/model'

const modules = import.meta.glob('../../views/**/**.vue')
const Layout = () => import('@/layout/index.vue')

/**
 * Use meta.role to determine if the current user has permission
 *
 * @param roles 用户角色集合
 * @param route 路由
 * @returns
 */
const hasPermission = (roles: string[], route: RouteRecordRaw) => {
  if (route.meta && route.meta.roles) {
    // 角色【超级管理员】拥有所有权限,忽略校验
    if (roles.includes('ROOT')) {
      return true
    }
    return roles.some(role => {
      if (route.meta?.roles) {
        return route.meta.roles.includes(role)
      }
    })
  }
  return false
}

/**
 * 递归过滤有权限的动态路由
 *
 * @param routes 接口返回所有的动态路由
 * @param roles 用户角色集合
 * @returns 返回用户有权限的动态路由
 */
const filterAsyncRoutes = (routes: RouteVO[], roles: string[]) => {
  const asyncRoutes: RouteRecordRaw[] = []
  routes.forEach(route => {
    const tmpRoute = { ...route } as RouteRecordRaw // 深拷贝 route 对象 避免污染
    if (hasPermission(roles, tmpRoute)) {
      // 如果是顶级目录,替换为 Layout 组件
      if (tmpRoute.component?.toString() == 'Layout') {
        tmpRoute.component = Layout
      } else {
        // 如果是子目录,动态加载组件
        const component = modules[`../../views/${tmpRoute.component}.vue`]
        if (component) {
          tmpRoute.component = component
        } else {
          tmpRoute.component = modules[`../../views/error-page/404.vue`]
        }
      }

      if (tmpRoute.children) {
        tmpRoute.children = filterAsyncRoutes(route.children, roles)
      }

      asyncRoutes.push(tmpRoute)
    }
  })

  return asyncRoutes
}
// setup
export const usePermissionStore = defineStore('permission', () => {
  // state
  const routes = ref<RouteRecordRaw[]>([])

  // actions
  function setRoutes(newRoutes: RouteRecordRaw[]) {
    routes.value = constantRoutes.concat(newRoutes)
  }

  /**
   * 生成动态路由
   *
   * @param roles 用户角色集合
   * @returns
   */
  function generateRoutes(roles: string[]) {
    return new Promise<RouteRecordRaw[]>((resolve, reject) => {
      // 接口获取所有路由
      MenuAPI.getRoutes()
        .then(data => {
          // 过滤有权限的动态路由
          const accessedRoutes = filterAsyncRoutes(data, roles)
          setRoutes(accessedRoutes)
          resolve(accessedRoutes)
        })
        .catch(error => {
          reject(error)
        })
    })
  }

  /**
   * 获取与激活的顶部菜单项相关的混合模式左侧菜单集合
   */
  const mixLeftMenus = ref<RouteRecordRaw[]>([])
  function setMixLeftMenus(topMenuPath: string) {
    const matchedItem = routes.value.find(item => item.path === topMenuPath)
    if (matchedItem && matchedItem.children) {
      mixLeftMenus.value = matchedItem.children
    }
  }
  return {
    routes,
    setRoutes,
    generateRoutes,
    mixLeftMenus,
    setMixLeftMenus
  }
})

// 非setup
export function usePermissionStoreHook() {
  return usePermissionStore(store)
}

4. api/menu

上面的 MenuAPI.getRoutes() 就是在 api/menu 里面定义的接口请求

import request from "@/utils/request";
import { MenuQuery, MenuVO, MenuForm, RouteVO } from "./model";

class MenuAPI {
  /**
   * 获取路由列表
   */
  static getRoutes() {
    return request<any, RouteVO[]>({
      url: "/api/v1/menus/routes",
      method: "get",
    });
  }
  // ... 其它接口
export default MenuAPI;

5. plugins/permission

这个文件内也是一些权限配置,按钮鉴权,路由守卫都放在这里面了,主要看 setupPermission 中的 router.addRoute(route) 跳转时会把动态路由塞到原本路由表内

import router from '@/router'
import { useUserStore, usePermissionStore } from '@/store'
import NProgress from '@/utils/nprogress'
import { RouteRecordRaw } from 'vue-router'
import { TOKEN_KEY } from '@/enums/CacheEnum'

// 是否有权限
export function hasAuth(value: string | string[], type: 'button' | 'role' = 'button') {
  const { roles, perms } = useUserStore().user
  //「超级管理员」拥有所有的按钮权限
  if (type === 'button' && roles.includes('ROOT')) {
    return true
  }
  const auths = type === 'button' ? perms : roles
  return typeof value === 'string'
    ? auths.includes(value)
    : auths.some(perm => {
        return value.includes(perm)
      })
}

export function setupPermission() {
  // 白名单路由
  const whiteList = ['/login', '/404']

  router.beforeEach(async (to, from, next) => {
    NProgress.start()
    const hasToken = localStorage.getItem(TOKEN_KEY)
    if (hasToken) {
      if (to.path === '/login') {
        // 如果已登录,跳转首页
        next({ path: '/' })
        NProgress.done()
      } else {
        const userStore = useUserStore()
        const hasRoles = userStore.user.roles && userStore.user.roles.length > 0
        if (hasRoles) {
          // 未匹配到任何路由,跳转404
          if (to.matched.length === 0) {
            from.name ? next({ name: from.name }) : next('/404')
          } else {
            next()
          }
        } else {
          const permissionStore = usePermissionStore()
          try {
            const { roles } = await userStore.getUserInfo()
            const accessRoutes = await permissionStore.generateRoutes(roles)
            accessRoutes.forEach((route: RouteRecordRaw) => {
              router.addRoute(route)
            })
            next({ ...to, replace: true })
          } catch (error) {
            // 移除 token 并跳转登录页
            await userStore.resetToken()
            next(`/login?redirect=${to.path}`)
            NProgress.done()
          }
        }
      }
    } else {
      // 未登录可以访问白名单页面
      if (whiteList.indexOf(to.path) !== -1) {
        next()
      } else {
        next(`/login?redirect=${to.path}`)
        NProgress.done()
      }
    }
  })

  router.afterEach(() => {
    NProgress.done()
  })
}

差不多是这样的,大概页面就这样了
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/693185.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Hive on Spark版本兼容性

Hive on Spark仅在特定版本的Spark上进行测试&#xff0c;因此给定版本的Hive只能保证与特定版本的Spark一起工作。其他版本的Spark可能与给定版本的Hive一起工作&#xff0c;但不能保证。以下是Hive版本及其对应的Spark版本列表&#xff1a; 详情参考官方文档&#xff1a;http…

11.Spring AOP

文章目录 1.什么是 Spring AOP&#xff1f;2.为什要用 AOP&#xff1f;3.Spring AOP 应该怎么学习呢&#xff1f;3.1 AOP 组成3.1.1 切⾯&#xff08;Aspect&#xff09; 切点 通知3.1.2 连接点&#xff08;Join Point&#xff09;3.1.3 切点&#xff08;Pointcut&#xff09;…

53.ReentrantLock原理

ReentrantLock使用 ReentrantLock 实现了Lock接口&#xff0c; 内置了Sync同步器继承了AbstractQueuedSynchronizer。 Sync是抽象类&#xff0c;有两个实现NonfairSync非公平&#xff0c;FairSync公平。 所以ReentrantLock有公平锁和非公平锁。默认是非公平锁。 public sta…

[数据集][目标检测]足球场足球运动员身份识别足球裁判员数据集VOC+YOLO格式312张4类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;312 标注数量(xml文件个数)&#xff1a;312 标注数量(txt文件个数)&#xff1a;312 标注类别…

Nvidia/算能 +FPGA+AI大算力边缘计算盒子:电力巡检智能机器人

聚焦数字经济与双碳经济赛道&#xff0c;专注于提供集中式新能源场站与分布式综合能源数智化整体解决方案&#xff0c;坚持以场站数字化、综合能源数字化双轮驱动发展。依靠专业化人才队伍与丰富的实证基地研究经验&#xff0c;打造成熟、先进的数智新能源研发平台。 在集中式新…

linux本地搭建apt源

使用apt-mirror搭建 1.安装 apt-get install apt-mirror2.编辑配置文件 vi /etc/apt/mirror.list修改下载目录 set_base_path 后面改为下载镜像的目录&#xff0c;同时删除前面#号 添加同步的源&#xff0c;建议用国内的&#xff0c;速度快&#xff0c;我这里用的阿里云的镜…

Leetcode 力扣109. 有序链表转换二叉搜索树 (抖音号:708231408)

给定一个单链表的头节点 head &#xff0c;其中的元素 按升序排序 &#xff0c;将其转换为 平衡 二叉搜索树。 示例 1: 输入: head [-10,-3,0,5,9] 输出: [0,-3,9,-10,null,5] 解释: 一个可能的答案是[0&#xff0c;-3,9&#xff0c;-10,null,5]&#xff0c;它表示所示的高…

每日一题——Python实现PAT甲级1015 Reversible Primes(举一反三+思想解读+逐步优化)

一个认为一切根源都是“自己不够强”的INTJ 个人主页&#xff1a;用哲学编程-CSDN博客专栏&#xff1a;每日一题——举一反三Python编程学习Python内置函数 Python-3.12.0文档解读 目录 我的写法 is_prime函数分析&#xff1a; decimal_to_base函数分析&#xff1a; 主循…

无头+单向+非循环链表的实现

这里写目录标题 1. 链表1.1 链表的概念及结构1.2 链表的分类 2. 接口实现3. 链表的实现3.1 打印链表3.2 头插3.3 尾插3.4 头删3.5 尾删3.6 单链表查找3.7 在pos之前插入3.8 在pos之后插入3.9 删除pos位置的值3.10 删除pos位置之后的值3.11 链表的释放3.12 动态申请一个节点 4. …

STM32F103C8T6 HAL库串口重定向

前言&#xff1a; 这里仅用做个人记录&#xff0c;实现USART1串口通信&#xff0c;并通过printf重定向输出“串口打印测试” 正文开始&#xff1a; 首先在STM32CubeMX上对串口进行配置&#xff0c;其实方法也非常简单。 按照箭头顺序&#xff0c;先点击Connectivity找到USART1…

30分钟吃掉pytorch转onnx及推理

pytorch模型线上部署最常见的方式是转换成onnx然后再转成tensorRT 在cuda上进行部署推理。 本文介绍将pytorch模型转换成onnx模型并进行推理的方法。 #!pip install onnx #!pip install onnxruntime #!pip install torchvision 公众号算法美食屋后台回复关键词&#xff1a;源码…

jmeter -n -t 使用非GUI模式运行脚本说明

命令模式下执行jmx文件 jmeter -n -t fatie.jmx -l results\t4.jtl -e -o results\h1 表示以命令行模式运行当前目录下的脚本fatie.jmx,将结果存入当前目录下的results\t1.jtl,并且生成html格式的报告&#xff0c;写入文件夹results\h1。 说明&#xff1a;生成结果的文件夹r…

《精通ChatGPT:从入门到大师的Prompt指南》第10章:案例分析

第10章&#xff1a;案例分析 10.1 优秀Prompt案例解析 在深入探讨如何精通ChatGPT的使用之前&#xff0c;理解并分析一些优秀的Prompt案例是至关重要的。这不仅有助于更好地掌握Prompt的构建技巧&#xff0c;还能提高与AI交互的效果。在这一节中&#xff0c;我们将详细解析一…

实用的 C 盘搬家软件

一、简介 1、一款专门用于 Windows 系统的文件夹移动工具&#xff0c;它允许用户将程序或游戏的安装文件夹从一台驱动器移动到另一台驱动器&#xff0c;或者同一个驱动器内的不同路径&#xff0c;而无需重新安装或破坏现有的程序安装。 二、下载 1、下载地址&#xff1a; 官网链…

1-Maven-settings配置

1-Maven-settings配置 整理下Maven工具的使用。 【本地仓库、私服、镜像仓库、远程仓库、中央仓库】 本文基于阅读其他博客和对公司Maven配置的学习整理出来的。希望通过本此学习能对Maven有个整体性的掌控。 顺序&#xff1a;profile.repository > pom文件中的repository &…

关于焊点检测(SJ-BIST)模块实现

关于焊点检测&#xff08;SJ-BIST&#xff09;模块实现 语言 &#xff1a;Verilg HDL 、VHDL EDA工具&#xff1a;ISE、Vivado、Quartus II 关于焊点检测&#xff08;SJ-BIST&#xff09;模块实现一、引言二、焊点检测功能的实现方法&#xff08;1&#xff09; 输入接口&#x…

SpringBoot+Vue网上超市(前后端分离)

技术栈 JavaSpringBootMavenMySQLMyBatisVueShiroElement-UI 角色对应功能 用户管理员 功能截图

C基础与SDK调试方法

REVIEW 上次学习了一下软件使用流程zynq PS点灯-CSDN博客 本次学习一下C编程基础与调试方法 1. 硬件编程原理 小梅哥视频链接&#xff1a; 07_Xilinx嵌入式裸机硬件编程原理_哔哩哔哩_bilibili 对应的课程笔记&#xff1a;【zynq课程笔记】【裸机】【第7课 】【硬件编程原理…

eNSP学习——配置RIP路由附加度量值

目录 主要命令 原理概述 实验目的 实验内容 实验拓扑 实验编址 实验步骤 1、基本配置 2、搭建RIP网络 3、配置RIP Metricin 4、配置RIP Metricout 需要eNSP各种配置命令的点击链接自取&#xff1a;华为&#xff45;NSP各种设备配置命令大全PDF版_ensp配置命令大全资…

Vyper重入漏洞解析

什么是重入攻击 Reentrancy攻击是以太坊智能合约中最具破坏性的攻击之一。当一个函数对另一个不可信合约进行外部调用时&#xff0c;就会发生重入攻击。然后&#xff0c;不可信合约会递归调用原始函数&#xff0c;试图耗尽资金。 当合约在发送资金之前未能更新其状态时&#…