前端单独实现 vue 动态路由

news2024/11/24 17:45:25

前端单独实现 vue 动态路由

Vue 动态路由权限是指在 Vue 应用程序中,根据用户的权限动态生成和控制路由的行为。这意味着不是所有的路由都在应用启动时就被硬编码到路由配置中,而是根据用户的权限信息,在运行时动态地决定哪些路由应该被加载和显示。

动态路由的优点:

  • 安全性:

    • 只有经过验证的用户才能访问其权限范围内的页面。
    • 减少了由于硬编码路由导致的安全漏洞。
  • 灵活性:

    • 可以根据用户的权限动态调整应用的结构,无需重新部署整个应用即可调整路由。
    • 支持按需加载(懒加载),提高应用性能。
  • 用户体验:

    • 只展示用户可以访问的菜单项,避免显示无用链接,提高用户体验。
    • 用户界面更加简洁,只显示与其角色相关的功能。
  • 可维护性:

    • 简化了路由配置,因为不需要为每个角色单独编写路由配置,而是集中管理权限。
    • 更容易扩展和修改权限配置,只需更新前端的权限数据即可。
  • 开发效率:

    • 开发者只需要关注业务逻辑,而不需要关心每个角色的具体路由配置。
    • 减少了重复工作,提高了开发效率。

实现步骤

  • 定义静态路由配置:

    • 在项目中定义一个包含所有可能路由的静态配置文件或对象,每个路由可以附加权限信息(如角色、访问级别等)。
  • 用户登录与鉴权:

    • 用户登录时,前端存储用户的权限信息(如角色、权限列表等)。
  • 动态生成路由:

    • 根据用户的权限信息,从前端的静态路由配置中筛选出用户有权访问的路由。
    • 使用递归算法或其他逻辑动态生成路由配置,并添加到 Vue Router 实例中。
  • 动态渲染菜单:

    • 根据动态生成的路由表来渲染左侧菜单或顶部导航栏,确保只显示用户有权访问的菜单项。

代码示例

配置路由器

router/index.js

// router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Layout from '@/Layout/index.vue'


Vue.use(VueRouter)

// export const roleMap = {
//     '-1':'运维管理员',
//     '1':'普通用户',
//     '2':'项目经理',
//     '3':'部门管理员',
//     '4':'综合部管理员',
//     '5':'部门领导'
// }

// 公共路由
export const routes = [
  {
    path: '/',
    name: 'redirect',
    component: Layout,
    hidden: true, // 隐藏菜单
    redirect: "/homePage", // 用户在地址栏输入 '/' 时会自动重定向到 /homePage 页面
  },
  {
    path: '/homePage',
    component: Layout,
    redirect: "/homePage/index",
    meta: {
      title: "首页",
    },
    children: [
      {
        path: 'index',
        name: 'homePageIndex',
        meta: {
          title: "首页",
        },
        component: () => import('@/views/homePage/index.vue')
      }
    ]
  },
  {
    path: '/login',
    component: () => import('@/views/login.vue'),
    hidden: true
  },
  {
    path: '/404',
    component: () => import('@/views/error/404.vue'),
    hidden: true
  },
  {
    path: '/401',
    component: () => import('@/views/error/401.vue'),
    hidden: true
  },
]


// 动态权限路由
export const dynamicRoutes = [
  {
    path: '/admin',
    meta: {
      title: "系统管理",
    },
    component: Layout,
    permission: ['-1', '2', '3', '4', '5'], // all 所有角色都可以访问  1 普通用户  2 项目经理  3 部门管理员  4 综合部管理员  5  部门领导  -1 项目运维管理员
    children: [
      {
        path: 'user',
        name: 'userIndex',
        meta: {
          title: "用户管理",
        },
        permission: ['-1', '2', '3', '4', '5'],
        component: () => import('@/views/admin/user/index.vue')
      },
      {
        path: 'role',
        name: 'roleIndex',
        meta: {
          title: "角色管理",
        },
        permission: ['-1', '2', '3', '4', '5'],
        component: () => import('@/views/admin/role/index.vue'),
        children: [
          {
            path: 'add',
            name: 'addRole',
            meta: {
              title: "添加角色",
            },
            permission: ['-1',, '3', '4', '5'],
            component: () => import('@/views/admin/user/index.vue')
          },
          {
            path: 'update',
            name: 'updateRole',
            meta: {
              title: "编辑角色",
            },
            permission: ['-1', '2', '3', '4', '5'],
            component: () => import('@/views/admin/role/index.vue')
          }
        ]
      }
    ]
  },
  {
    path: '/tableEcho',
    meta: {
      title: "表格管理",
    },
    component: Layout,
    permission: ['-1', '1', '2'],
    children: [
      {
        path: 'test',
        name: 'tableEchoIndex',
        meta: {
          title: "表格测试",
        },
        permission: ['-1', '1', '2'],
        component: () => import('@/views/tableEcho/index.vue'),
        children: [
          {
            path: 'add',
            name: 'addTable',
            hidden: true,
            meta: {
              title: "新增测试",
            },
            permission: ['-1', '2'],
            component: () => import('@/views/tableEcho/add.vue')
          }
        ]
      },
    ],
  },
]

const router = new VueRouter({
  base: process.env.BASE_URL,
  routes
})

export default router

上述代码定义了一个公共路由 routes 和一个动态权限控制的路由 dynamicRoutes , permission 数组定义了哪些角色拥有该路由权限, 将用户分为6个角色级别, 每个角色对应不同的角色级别,分别为

  • ‘-1’:‘运维管理员’,
  • ‘1’:‘普通用户’,
  • ‘2’:‘项目经理’,
  • ‘3’:‘部门管理员’,
  • ‘4’:‘综合部管理员’,
  • ‘5’:‘部门领导’,

封装路由守卫

permission.js

// permission.js
import router from './router'
import store from './store'
import { Message } from 'element-ui'
import { getStore } from '@/utils/store';

const whiteList = ['/login', '/404', '/401'];

router.beforeEach((to, from, next) => {
  let token = getStore('token');

  if (token) {
    /* has token*/
    if (to.path === '/login') {
      next({ path: '/' });
    } else {
      if (store.getters.roles.length === 0) {
        // 判断当前用户是否已拉取完user_info信息
        store.dispatch('GetInfo').then((res) => {
          console.log('--------------', res);
          router.addRoutes(res) // 动态添加可访问路由表
          next({ ...to, replace: true }) // hack方法 确保addRoutes已完成
        }).catch(err => {
          store.dispatch('LogOut').then(() => {
            Message.error(err)
            next(`/`)
          })
        })
      } else {
        next()
      }
    }
  } else {
    // 没有token
    if (whiteList.indexOf(to.path) !== -1) {
      // 在免登录白名单,直接进入
      next()
    } else {
      next(`/login`) // 否则全部重定向到登录页
    }
  }
})

上述代码表示在路由的 beforeEach 函数里面调用 vuex 里面 actions 里的方法发送接口请求获取用户信息与用户角色权限, 最后通过 router.addRoutes(res) 渲染路由
permission.js 文件需引入到 main.js里面

如果项目 vue-router 版本超过 3.3.0, 需要遍历路由数组再使用 router.addRoute() 方法逐个添加路由

res.forEach( route => {
	router.addRoute(route);
})

在 vuex 里获取用户所拥有的权限, 过滤该权限不拥有的路由

store/index.js

// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import { routes, dynamicRoutes } from "@/router";
import { login, getInfo, logout } from "@/api/user";
import { setStore, clearStore } from '@/utils/store';


Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    routes,
    token: "",
    roleType: "",
    roles: [],
    permissions: [],
    sidebarRouters: [],

  },
  getters: {
    token: state => state.token,
    roles: state => state.roles,
    permissions: state => state.permissions,
    sidebarRouters: state => state.sidebarRouters,
  },
  mutations: {
    SET_TOKEN: (state, token) => {
      state.token = token;
    },
    SET_USERINFO: (state, user) => {
      state.userInfo = user;
    },
    SET_ROLETYPE: (state, roleType) => {
      state.roleType = roleType;
    },
    SET_ROLES: (state, roles) => {
      state.roles = roles;
    },
    SET_PERMISSIONS: (state, permissions) => {
      state.permissions = permissions;
    },
    SET_ROUTE: (state, sidebarRouters) => {
      state.sidebarRouters = sidebarRouters;
    },
  },
  actions: {
    Login({ commit }, userInfo) {
      return new Promise((resolve, reject) => {
        login(userInfo).then(res => {
          setToken(res.data.token);
          setStore('token', res.data.token);
          commit('SET_TOKEN', res.data.token);
          resolve();
        }).catch(error => {
          reject(error);
        })
      })
    },
    // 获取用户信息
    GetInfo({ commit }) {
      return new Promise((resolve, reject) => {
        getInfo().then(res => {
          console.log('res::: ', res);
          if (res.data.code === 0 || 200) {
            const user = res.data.sysUser;
            const roleType = res.data.roleType;
            commit('SET_USERINFO', user);

            // roleType 用户所用的权限级别 1 普通用户  2 项目经理  3 部门管理员  4 综合部管理员  5  部门领导  -1 项目运维管理员
            setStore('ROLE_TYPE', roleType);
            if (res.data.roles) { // 验证返回的roles是否为真
              commit('SET_ROLES', res.data.roles);
              commit('SET_PERMISSIONS', res.data.permissions);
            } else {
              commit('SET_ROLES', ['ROLE_DEFAULT']);
            }
            // 过滤路由
            let newRouters = filterRouter(roleType, dynamicRoutes);

            // 连接公共路由
            const sidebarRouters = routes.concat([...newRouters])

            commit('SET_ROUTE', sidebarRouters);

            resolve(sidebarRouters);
          } else {
            reject(error);
          }
        }).catch(error => {
          reject(error);
        })
      })
    },
    // 退出系统
    LogOut({ commit, state }) {
      return new Promise((resolve, reject) => {
        logout(state.token).then(() => {
          commit('SET_TOKEN', '')
          commit('SET_ROLES', [])
          commit('SET_PERMISSIONS', [])
          clearStore('token');
          clearStore('userInfo')
          resolve()
        }).catch(error => {
          reject(error)
        })
      })
    },
  },
  modules: {
  }
})

function filterRouter(roleType, routes) {
  return routes.filter(item => {  // filter 方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素,测试未通过的元素会自动剔除
    // 如果一级路由的 permission 含有当前角色的 roleType
    if (item.permission.includes(roleType)) {

      // 如果该一级路由含有子路由时,递归调用该函数判断子路由是否有权限
      if (Array.isArray(item.children) && item.children.length > 0) {

        // 递归调用该函数,最后接受校验通过后的子路由
        let newChildren = filterRouter(roleType, item.children);

        // 如果子路由有值,则赋值给当前路由的 children,剔除校验不通过的子路由
        if (newChildren.length > 0) {
          item.children = newChildren;
        } else if (newChildren.length === 0) { // 如果子路由为空,则删除该路由的 children 属性
          delete item.children;
        }
      }

      // 最后返回 true, 表示该路由通过权限校验
      return true;
    }
  })
}

上述代码通过 getInfo 接口获取用户权限, 通过函数 filterRouter 过滤掉该角色不拥有的路由, 通过 concat 方法合并 routes 公共路由, 最后通过 resolve 返回

文件布局如下

在这里插入图片描述

下图为页面渲染的菜单(项目经理角色)

在这里插入图片描述

左侧菜单实现参考链接: Elemnt-UI + 递归组件实现后台管理系统左侧菜单

前端结合后端接口请求实现动态路由参考连接: 前端 + 接口请求实现 vue 动态路由

总结

在用户登录成功后从服务器获取用户的权限信息,在 vuex 的异步处理函数中过滤掉角色权限不存在的路由,使用 concat() 合并公共路由,最后使用 router.addRoutes(res) 动态添加可访问的路由。这样可以确保应用根据用户的权限动态加载相应的路由,增强安全性与灵活性。

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

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

相关文章

3. 轴指令(omron 机器自动化控制器)——>MC_PowerMC_MoveJog

机器自动化控制器——第三章 轴指令 1 MC_Power变量▶输入变量▶输出变量▶输入输出变量 功能说明▶时序图▶重启运动指令▶多重启动运动指令▶错误代码 MC_MoveJog变量▶输入变量▶输出变量▶输入输出变量 功能说明▶时序图▶重启运动指令▶多重启动运动指令▶异常 MC_Power …

从0书写一个softmax分类 李沐pytorch实战

输出维度 在softmax 分类中 我们输出与类别一样多。 数据集有10个类别,所以网络输出维度为10。 初始化权重和偏置 torch.norma 生成一个均值为 0,标准差为0.01,一个形状为size(num_inputs, num_outputs)的张量偏置生成一个num_outputs 10 的一维张量&a…

1265:【例9.9】最长公共子序列 动态规划

题目链接 题目: 思路 最长-最值问题、重叠子问题、最优结构-前面序列的公共序列最优值是后续序列的子问题、无后效性也满足 确定状态、变量:序列是没有要求要连续,因此只能用长度为i的串a分别和长度为(1-j)串b去找最值…

【Linux】:信号与信号产生

朋友们、伙计们,我们又见面了,本期来给大家带来信号和信号的产生相关代码和知识点,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成! C 语 言 专 栏:C语言:从入门到…

HarmonyOS开发实战( Beta5.0)日历切换案例实践详解

鸿蒙HarmonyOS开发往期必看: HarmonyOS NEXT应用开发性能实践总结 最新版!“非常详细的” 鸿蒙HarmonyOS Next应用开发学习路线!(从零基础入门到精通) 介绍 本示例介绍使用Swiper实现自定义日历月视图和周视图左右滑…

反编译app

反编译代码步骤: 1.用dex2jar 将apk打成jar,d2j-dex2jar your-app.apk GitHub - pxb1988/dex2jar: Tools to work with android .dex and java .class filesTools to work with android .dex and java .class files - pxb1988/dex2jarhttps://github.co…

注解实现json序列化的时候自动进行数据脱敏

最近在进行开发的时候遇到一个问题,需要对用户信息进行脱敏处理,原有的方式是写一个util类,在需要脱敏的字段查出数据后,显示掉用方法处理后再set回去,觉得这种方式能实现功能,但是不是特别优雅&#xff0c…

机器学习特征分析

机器学习的常规流程 在真正进入机器学习算法之前,数据准备和处理过程会尤为重要,这直接关系到后续模型的效果和最终的业务判决。 数据分析 什么是数据分析 数据分析指对原始数据进行检查、清理、转换及筛选等一系列动作,找到数据对结果的影…

Qwen1.5模型文本分类微调实战教程

大家好啊!今天咱们来聊聊怎么给大语言模型"调教"一下,让它在文本分类这个任务上玩得更溜。具体来说,我们要用Qwen1.5这个模型来做文章。别看这活儿听着高大上,其实做起来也没那么难。跟着我来,保证让你轻松上手! 咱们这…

How to fool AI content detectors?

Add prompt below: Make it sound like a tweed jacket wearing professor taking to a group of 20 years old students. Vary the sentences length. Make it persoanl, add a touch of humor. Make the blog post sound unique when compared to Other blog posts.

MySQL--库的操作

文章目录 1.创建数据库2.创建数据库案例3.字符集和校验规则3.1默认字符集3.2默认校验规则3.3查看系统默认字符集以及校验规则3.4查看数据库支持的字符3.5查看数据库支持的字符集校验规则3.6校验规则对数据库的影响不区分大小写查询:排序结果:区分大小写查…

BFS广度优先搜索和DFS深度优先搜索解决迷宫问题

前言 BFS广度优先搜索和DFS深度优先搜索解决迷宫问题 迷宫问题 原题目:迷宫由n行m列的单元格组成(n,m都小于等于50),每个单元格要吗是空地要吗是障碍物。现在请你找到一条从起点到终点的最短路径长度。 分析 BFS广度优先搜索 首先我们将起点入队&a…

iOS 18 RC 版本更新,为相机应用引入了“暂停录制视频”功能

苹果公司9月10日正式向全球iPhone用户推送了iOS 18 Release Candidate(RC)版本。这一版本的发布不仅标志着iOS系统的又一次重大更新,更预示着苹果在提升用户体验、增强隐私保护以及推动AI应用方面的持续努力。 并且此次苹果公司最新推出的 i…

Unity基本操作

API手册 Unity 脚本 APIhttps://docs.unity.cn/cn/2022.3/ScriptReference/index.html 在遇到不懂的方法、想更深入的学习或者是想查看是否有相应的方法实现某项功能,可以在Unity官方这里查看脚本。以Transform为例,可以直接搜索,或者在Unit…

9月12日 QT

//设置图片缩放适应label ui->label->setScaledContents(true); // 在spinbox后方设置$特殊符号 ui->spinBox->setSuffix(" 斤"); //给肉类combobox加入项目 QStringList Meat_List{"请选择&quo…

数据放到GPU上,运行程序卡住检查方法

这个问题一定是要结合具体的代码,下面就自己遇到问题,询问chatGPT后发现问题所在的过程进行记录,当然绝大部分情况下都是batch_size设置太大了,显卡内存不足导致 部分重点代码: 导入模型部分略 #自定义数据集有关类 c…

无人机 PX4 飞控 | EKF2简介与使用方法

无人机 PX4 飞控 | EKF2简介与使用方法 PX4 EKF2简介EKF 的启动ecl EKF 的优缺点缺点优点 运行单个EKF实例运行多个EKF实例 PX4 EKF2简介 PX4是一个流行的开源飞控系统,广泛用于无人机和其他自动驾驶飞行器。EKF2(Extended Kalman Filter 2)…

IEEE 802.11a OFDM系统的仿真(续)

(内容源自详解MATLAB/SIMULINK 通信系统建模与仿真 刘学勇编著第九章内容,有兴趣的读者请阅读原书) clear all %%%%%%%参数设计部分%%%%%%%Nsp52;%系统子载波数(不包括直流载波) Nfft64;%FFT长度 Ncp16;…

ppt文档怎么转换成pdf?快来试试这几种转换方法!

ppt文档怎么转换成pdf?在日常工作与学习的广阔舞台上,PPT,这一演示文稿的常青树,无疑是表达创意、传递信息的重要工具,然而,正如每枚硬币都有其两面,PPT在带来便捷的同时,也显露出一…

js | TypeError: Cannot read properties of null (reading ‘indexOf’) 【解决】

js | TypeError: Cannot read properties of null (reading ‘indexOf’) 【解决】 描述 概述 在前端开发中,遇到TypeError: Cannot read properties of null (reading indexOf)这类错误并不罕见。这个错误通常表明你试图在一个null值上调用indexOf方法&#xff0c…