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

news2024/11/17 3:46:34

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.coloradmin.cn/o/1788579.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue3+three.js给glb模型设置视频贴图

1.在网上下载一个显示屏或者自己画一个,在blender中设置好显示屏的Mesh,UV设置好,这样方便代码中添加纹理贴图。可以让美术在建模软件中,先随机设置一张图片作为纹理,验证UV是否设置好 关于如何 在blender中给模型设置UV贴图百度很多的 // 视频 import * as THREE from…

直播回顾丨GQL 与新版本悦数图数据库亮点解析

5 月 23 日&#xff0c;悦数图数据库产品总监方扬亲临直播间&#xff0c;为我们深入剖析了 GQL 的技术内核&#xff0c;以及它如何引领图数据库技术的全新变革。同时&#xff0c;还揭秘了新版悦数图数据库的众多技术特点&#xff0c;让人眼前一亮。 添加图片注释&#xff0c;不…

python的模块

什么是模块&#xff08;Module&#xff09; 在计算机程序的开发过程中&#xff0c;随着程序代码越写越多&#xff0c;写在一个文件里的代码就会越来越长&#xff0c;越来越不容易维护。 为了让代码方便维护&#xff0c;我们将代码进行分类&#xff0c;分别放到不同的文件里。…

Xcode中给UIView在xib中添加可视化的属性

给UIView在xib中添加可视化的属性 效果如下图&#xff1a; 可以直接设置view 的 borderColor 、borderWidth、cornerRadius&#xff0c;也可以单独指定view的某个角是圆角。减少了代码中的属性。 完整代码&#xff1a; UIViewBorder.h #import <UIKit/UIKit.h>inter…

Python - 深度学习系列38 重塑实体识别5-预测并行化改造

说明 在重塑实体识别4中梳理了数据流&#xff0c;然后我发现pipeline的串行效率太低了&#xff0c;所以做了并行化改造。里面还是有不少坑的&#xff0c;记录一下。 内容 1 pipeline 官方的pipeline看起来的确是比较好用的&#xff0c;主要是实现了比较好的数据预处理。因为…

【全网唯一】触摸精灵iOS版纯离线本地文字识别插件

目的 触摸精灵iOS是一款可以模拟鼠标和键盘操作的自动化工具。它可以帮助用户自动完成一些重复的、繁琐的任务&#xff0c;节省大量人工操作的时间。但触摸精灵的图色功能比较单一&#xff0c;无法识别屏幕上的图像&#xff0c;根据图像的变化自动执行相应的操作。本篇文章主要…

电脑没电关机,wsl和docker又挂了,附解决过程

如题&#xff0c;开了个会没带笔记本电源&#xff0c;点啊弄关机后docker打不开&#xff0c;我以为是docker坏了&#xff0c;结果docker报错&#xff1a; An unexpected error occurred while executing a WSL command. Either shut down WSL down with wsl --shutdown, and/or…

day32--Spring(一)

一、Spring简介 1 Spring课程介绍 问题导入 我们为什么要学习Spring框架&#xff1f; 1.1 为什么要学 Spring技术是JavaEE开发必备技能&#xff0c;企业开发技术选型命中率>90% 专业角度 简化开发&#xff0c;降低企业级开发的复杂性框架整合&#xff0c;高效整合其他技…

概率分布、回归分析、假设检验……用 DolphinDB 函数库快速实现概率统计分析

在金融和物联网等领域&#xff0c;概率统计与分析扮演着至关重要的角色。DolphinDB 作为一款强大的时序数据库&#xff0c;提供了一系列内置的概率统计与分析函数&#xff0c;能够满足用户的各种需求。 金融领域 风险管理&#xff1a;通过概率统计分析&#xff0c;金融机构可…

python数据分析——逻辑回归

参考资料&#xff1a;活用pandas库 逻辑回归 当响应变量为二值响应变量时&#xff0c;经常使用逻辑回归对数据建模。 # 导入pandas库 import pandas as pd # 导入数据集 acspd.read_csv(r"...\data\acs_ny.csv") # 展示数据列 print(acs.columns) # 展示数据集 pri…

进程间通信(27000字超详解)

&#x1f30e;进程间通信 文章目录&#xff1a; 进程间通信 进程间通信简介       进程间通信目的       初识进程间通信       进程间通信的分类 匿名管道通信       认识管道       匿名管道       匿名管道测试       管道的四种…

免费,Scratch蓝桥杯比赛历年真题--第15届蓝桥杯STEMA真题-2024年3月份(含答案解析和代码)

第15届蓝桥杯STEMA真题-2024年3月份 一、单选题 答案&#xff1a;D 解析&#xff1a;y坐标正值表示上&#xff0c;负值表示下&#xff0c;故答案为D。 答案&#xff1a;C 解析&#xff1a;18<25为真&#xff0c;或关系表示一真即为真&#xff0c;故答案为C。 答案&#xff…

2024蓝桥杯初赛决赛pwn题全解

蓝桥杯初赛决赛pwn题解 初赛第一题第二题 决赛getting_startedbabyheap 初赛 第一题 有system函数&#xff0c;并且能在bss上读入字符 而且存在栈溢出&#xff0c;只要过掉check函数即可 check函数中&#xff0c;主要是对system常规获取权限的参数&#xff0c;进行了过滤&…

linux部署运维3——centos7下导入导出mysql数据库的sql文件以及查询数据量最大的表信息

在实际项目开发或者项目运维过程中&#xff0c;数据库的导入导出操作比较频繁&#xff0c;如果可以借助第三方工具那当然算喜事一桩&#xff1b;但是如果不允许外部访问&#xff0c;那么就只能使用数据库自带的命令&#xff0c;也是相当方便的。 一.导入sql文件 1.在linux命令…

Asp.Net Core 实现分片下载的最简单方式

技术群里的朋友遇到了这个问题&#xff0c;起初的原因是他对文件增加了一个属性配置 fileResult.EnableRangeProcessing true;这个属性我从未遇到过&#xff0c;然后&#xff0c;去F1查看这个属性的描述信息也依然少的可怜&#xff0c;只有简单的描述为(获取或设置为 启用范围…

CCIG 2024:大模型技术及其前沿应用论坛深度解析

一、CCIG论坛介绍 中国图象图形大会&#xff08;CCIG 2024&#xff09;是一场备受瞩目的学术盛会&#xff0c;近期在陕西省西安市曲江国际会议中心举行。这次会议以“图聚智生&#xff0c;象合慧成”为主题&#xff0c;由中国图象图形学学会主办&#xff0c;旨在汇聚图像图形领…

一篇文章讲透数据结构之树and二叉树

一.树 1.1树的定义 树是一种非线性的数据结构&#xff0c;它是有n个有限结点组成的一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树&#xff0c;也就是说它是根在上&#xff0c;叶在下的。 在树中有一个特殊的结点&#xff0c;称为根结点&#xff0c;根结点…

从0开始制作微信小程序

目录 前言 正文 需要事先准备的 需要事先掌握的 什么是uniapp 平台应用的分类方式 什么是TypeScript 创建项目 项目文件作用 源码地址 尾声 &#x1f52d; Hi,I’m Pleasure1234&#x1f331; I’m currently learning Vue.js,SpringBoot,Computer Security and so on.&#x1…

大数据之CDH对Hdfs做Balance数据均衡/数据平衡/数据倾斜

问题的来源: 由于在hive工具运行sql,出现sql卡顿的情况,去cdh上查看yarn资源的分布情况,发现了整个cdh平台中hdfs和yarn资源分布不均匀,大量的爆红显示: 以下 DataNode 数据目录 位于小于其可用空间 10.0 吉字节 的文件系统中。 /data1/dfs/dn&#xff08;可用&#xff1a;7.2 …

(九)Spring教程——ApplicationContext中Bean的生命周期

1.前言 ApplicationContext中Bean的生命周期和BeanFactory中的生命周期类似&#xff0c;不同的是&#xff0c;如果Bean实现了org.springframework.context.ApplicationContextAware接口&#xff0c;则会增加一个调用该接口方法setApplicationContext()的步骤。 此外&#xff0c…