Vue动态路由使用(后端控制)

article/2025/9/18 2:08:14

使用VUE开发后台管理系统 完全由后端控制左边菜单项思路

在传统开发后台管理系统时,都会涉及权限控制这一功能需求 即:根据不同登录的角色账号来使用该账号拥有的功能,也就是说系统左边的菜单栏不是固定不变的,而是根据登录账号的权限去动态控制的,现在主流的两种模式
即:1.前后端配合控制 2.完全由后端来控制

本章着重来介绍 第二种模式 :由后端数据控制前端的菜单

  1. 借助Ant Design Pro Vue 来讲解实现思路 (Ant-Design-Pro Vue 开箱即用的企业后台管理系统)
  2. 码云地址 :https://gitee.com/sendya/ant-design-pro-vue.git

一.实现思路

  1. 后端提供路由接口 前端请求数据然后二次处理

  2. 前端请求到的数据在这里插入图片描述

  3. 前端处理数据的逻辑

// eslint-disable-next-line
import * as loginService from '@/api/login'
// eslint-disable-next-line
import { BasicLayout, BlankLayout, PageView, RouteView } from '@/layouts'// 前端路由表  
const constantRouterComponents = {// 基础页面 layout 必须引入BasicLayout: BasicLayout,BlankLayout: BlankLayout,RouteView: RouteView,PageView: PageView,'403': () => import(/* webpackChunkName: "error" */ '@/views/exception/403'),'404': () => import(/* webpackChunkName: "error" */ '@/views/exception/404'),'500': () => import(/* webpackChunkName: "error" */ '@/views/exception/500'),// 你需要动态引入的页面组件'Workplace': () => import('@/views/dashboard/Workplace'),'Analysis': () => import('@/views/dashboard/Analysis'),// form'BasicForm': () => import('@/views/form/basicForm'),'StepForm': () => import('@/views/form/stepForm/StepForm'),'AdvanceForm': () => import('@/views/form/advancedForm/AdvancedForm'),// // list'TableList': () => import('@/views/list/TableList'),// 'StandardList': () => import('@/views/list/StandardList'),'CardList': () => import('@/views/list/CardList'),'SearchLayout': () => import('@/views/list/search/SearchLayout'),'SearchArticles': () => import('@/views/list/search/Article'),'SearchProjects': () => import('@/views/list/search/Projects'),'SearchApplications': () => import('@/views/list/search/Applications'),'ProfileBasic': () => import('@/views/profile/basic'),'ProfileAdvanced': () => import('@/views/profile/advanced/Advanced'),// result'ResultSuccess': () => import(/* webpackChunkName: "result" */ '@/views/result/Success'),'ResultFail': () => import(/* webpackChunkName: "result" */ '@/views/result/Error'),// exception'Exception403': () => import(/* webpackChunkName: "fail" */ '@/views/exception/403'),'Exception404': () => import(/* webpackChunkName: "fail" */ '@/views/exception/404'),'Exception500': () => import(/* webpackChunkName: "fail" */ '@/views/exception/500'),// account'AccountCenter': () => import('@/views/account/center'),'AccountSettings': () => import('@/views/account/settings/Index'),'BaseSettings': () => import('@/views/account/settings/BaseSetting'),'SecuritySettings': () => import('@/views/account/settings/Security'),'CustomSettings': () => import('@/views/account/settings/Custom'),'BindingSettings': () => import('@/views/account/settings/Binding'),'NotificationSettings': () => import('@/views/account/settings/Notification'),'TestWork': () => import(/* webpackChunkName: "TestWork" */ '@/views/dashboard/TestWork')
}// 前端未找到页面路由(固定不用改)
const notFoundRouter = {path: '*', redirect: '/404', hidden: true
}// 根级菜单
const rootRouter = {key: '',name: 'index',path: '',component: 'BasicLayout',redirect: '/dashboard',meta: {title: '首页'},children: []
}/*** 动态生成菜单* @param token* @returns {Promise<Router>}*/
export const generatorDynamicRouter = (token) => {return new Promise((resolve, reject) => {loginService.getCurrentUserNav(token).then(res => {console.log('res', res) //后端请求到的数据const { result } = resconst menuNav = []const childrenNav = []//      后端数据, 根级树数组,  根级 PIDlistToTree(result, childrenNav, 0) //路由数据 第一次处理rootRouter.children = childrenNav  menuNav.push(rootRouter)console.log('menuNav', menuNav)const routers = generator(menuNav) //路由数据 第二次处理routers.push(notFoundRouter)console.log('routers', routers)resolve(routers)}).catch(err => {reject(err)})})
}/*** 格式化树形结构数据 生成 vue-router 层级路由表** @param routerMap* @param parent* @returns {*}*///路由数据第二次处理  匹配加载组件 生成路由path 
export const generator = (routerMap, parent) => {   return routerMap.map(item => {const { title, show, hideChildren, hiddenHeaderContent, target, icon } = item.meta || {}const currentRouter = {// 如果路由设置了 path,则作为默认 path,否则 路由地址 动态拼接生成如 /dashboard/workplacepath: item.path || `${parent && parent.path || ''}/${item.key}`,// 路由名称,建议唯一name: item.name || item.key || '',// 该路由对应页面的 组件 :方案1// component: constantRouterComponents[item.component || item.key],// 该路由对应页面的 组件 :方案2 (动态加载)component: (constantRouterComponents[item.component || item.key]) || (() => import(`@/views/${item.component}`)),// meta: 页面标题, 菜单图标, 页面权限(供指令权限用,可去掉)meta: {title: title,icon: icon || undefined,hiddenHeaderContent: hiddenHeaderContent,target: target,permission: item.name}}// 是否设置了隐藏菜单if (show === false) {currentRouter.hidden = true}// 是否设置了隐藏子菜单if (hideChildren) {currentRouter.hideChildrenInMenu = true}// 为了防止出现后端返回结果不规范,处理有可能出现拼接出两个 反斜杠if (!currentRouter.path.startsWith('http')) {currentRouter.path = currentRouter.path.replace('//', '/')}// 重定向item.redirect && (currentRouter.redirect = item.redirect)// 是否有子菜单,并递归处理if (item.children && item.children.length > 0) {// RecursioncurrentRouter.children = generator(item.children, currentRouter)}return currentRouter})
}/*** 数组转树形结构* @param list 源数组* @param tree 树* @param parentId 父ID*/// 路由数据第一次 处理 将list结构转化为 tree
const listToTree = (list, tree, parentId) => {list.forEach(item => {// 判断是否为父级菜单if (item.parentId === parentId) {const child = {...item,key: item.key || item.name,children: []}// 迭代 list, 找到当前菜单相符合的所有子菜单listToTree(list, child.children, item.id)// 删掉不存在 children 值的属性if (child.children.length <= 0) {delete child.children}// 加入到树中tree.push(child)}})
}

最终生成的 路由数据
在这里插入图片描述

{path: '/',name: 'index',component: BasicLayout,meta: { title: 'menu.home' },redirect: '/dashboard/workplace',children: [// dashboard{path: '/dashboard',name: 'dashboard',redirect: '/dashboard/workplace',component: RouteView,meta: { title: 'menu.dashboard', keepAlive: true, icon: bxAnaalyse, permission: [ 'dashboard' ] },children: [{path: '/dashboard/analysis/:pageNo([1-9]\\d*)?',name: 'Analysis',component: () => import('@/views/dashboard/Analysis'),meta: { title: 'menu.dashboard.analysis', keepAlive: false, permission: [ 'dashboard' ] }},// 外部链接{path: 'https://www.baidu.com/',name: 'Monitor',meta: { title: 'menu.dashboard.monitor', target: '_blank' }},{path: '/dashboard/workplace',name: 'Workplace',component: () => import('@/views/dashboard/Workplace'),meta: { title: 'menu.dashboard.workplace', keepAlive: true, permission: [ 'dashboard' ] }}]},// forms{path: '/form',redirect: '/form/base-form',component: RouteView,meta: { title: '表单页', icon: 'form', permission: [ 'form' ] },children: [{path: '/form/base-form',name: 'BaseForm',component: () => import('@/views/form/basicForm'),meta: { title: '基础表单', keepAlive: true, permission: [ 'form' ] }},{path: '/form/step-form',name: 'StepForm',component: () => import('@/views/form/stepForm/StepForm'),meta: { title: '分步表单', keepAlive: true, permission: [ 'form' ] }},{path: '/form/advanced-form',name: 'AdvanceForm',component: () => import('@/views/form/advancedForm/AdvancedForm'),meta: { title: '高级表单', keepAlive: true, permission: [ 'form' ] }}]},// list{path: '/list',name: 'list',component: RouteView,redirect: '/list/table-list',meta: { title: '列表页', icon: 'table', permission: [ 'table' ] },children: [{path: '/list/table-list/:pageNo([1-9]\\d*)?',name: 'TableListWrapper',hideChildrenInMenu: true, // 强制显示 MenuItem 而不是 SubMenucomponent: () => import('@/views/list/TableList'),meta: { title: '查询表格', keepAlive: true, permission: [ 'table' ] }},{path: '/list/basic-list',name: 'BasicList',component: () => import('@/views/list/BasicList'),meta: { title: '标准列表', keepAlive: true, permission: [ 'table' ] }},{path: '/list/card',name: 'CardList',component: () => import('@/views/list/CardList'),meta: { title: '卡片列表', keepAlive: true, permission: [ 'table' ] }},{path: '/list/search',name: 'SearchList',component: () => import('@/views/list/search/SearchLayout'),redirect: '/list/search/article',meta: { title: '搜索列表', keepAlive: true, permission: [ 'table' ] },children: [{path: '/list/search/article',name: 'SearchArticles',component: () => import('../views/list/search/Article'),meta: { title: '搜索列表(文章)', permission: [ 'table' ] }},{path: '/list/search/project',name: 'SearchProjects',component: () => import('../views/list/search/Projects'),meta: { title: '搜索列表(项目)', permission: [ 'table' ] }},{path: '/list/search/application',name: 'SearchApplications',component: () => import('../views/list/search/Applications'),meta: { title: '搜索列表(应用)', permission: [ 'table' ] }}]}]},// profile{path: '/profile',name: 'profile',component: RouteView,redirect: '/profile/basic',meta: { title: '详情页', icon: 'profile', permission: [ 'profile' ] },children: [{path: '/profile/basic',name: 'ProfileBasic',component: () => import('@/views/profile/basic'),meta: { title: '基础详情页', permission: [ 'profile' ] }},{path: '/profile/advanced',name: 'ProfileAdvanced',component: () => import('@/views/profile/advanced/Advanced'),meta: { title: '高级详情页', permission: [ 'profile' ] }}]},// result{path: '/result',name: 'result',component: RouteView,redirect: '/result/success',meta: { title: '结果页', icon: 'check-circle-o', permission: [ 'result' ] },children: [{path: '/result/success',name: 'ResultSuccess',component: () => import(/* webpackChunkName: "result" */ '@/views/result/Success'),meta: { title: '成功', keepAlive: false, hiddenHeaderContent: true, permission: [ 'result' ] }},{path: '/result/fail',name: 'ResultFail',component: () => import(/* webpackChunkName: "result" */ '@/views/result/Error'),meta: { title: '失败', keepAlive: false, hiddenHeaderContent: true, permission: [ 'result' ] }}]},// Exception{path: '/exception',name: 'exception',component: RouteView,redirect: '/exception/403',meta: { title: '异常页', icon: 'warning', permission: [ 'exception' ] },children: [{path: '/exception/403',name: 'Exception403',component: () => import(/* webpackChunkName: "fail" */ '@/views/exception/403'),meta: { title: '403', permission: [ 'exception' ] }},{path: '/exception/404',name: 'Exception404',component: () => import(/* webpackChunkName: "fail" */ '@/views/exception/404'),meta: { title: '404', permission: [ 'exception' ] }},{path: '/exception/500',name: 'Exception500',component: () => import(/* webpackChunkName: "fail" */ '@/views/exception/500'),meta: { title: '500', permission: [ 'exception' ] }}]},// account{path: '/account',component: RouteView,redirect: '/account/center',name: 'account',meta: { title: '个人页', icon: 'user', keepAlive: true, permission: [ 'user' ] },children: [{path: '/account/center',name: 'center',component: () => import('@/views/account/center'),meta: { title: '个人中心', keepAlive: true, permission: [ 'user' ] }},{path: '/account/settings',name: 'settings',component: () => import('@/views/account/settings/Index'),meta: { title: '个人设置', hideHeader: true, permission: [ 'user' ] },redirect: '/account/settings/base',hideChildrenInMenu: true,children: [{path: '/account/settings/base',name: 'BaseSettings',component: () => import('@/views/account/settings/BaseSetting'),meta: { title: '基本设置', hidden: true, permission: [ 'user' ] }},{path: '/account/settings/security',name: 'SecuritySettings',component: () => import('@/views/account/settings/Security'),meta: { title: '安全设置', hidden: true, keepAlive: true, permission: [ 'user' ] }},{path: '/account/settings/custom',name: 'CustomSettings',component: () => import('@/views/account/settings/Custom'),meta: { title: '个性化设置', hidden: true, keepAlive: true, permission: [ 'user' ] }},{path: '/account/settings/binding',name: 'BindingSettings',component: () => import('@/views/account/settings/Binding'),meta: { title: '账户绑定', hidden: true, keepAlive: true, permission: [ 'user' ] }},{path: '/account/settings/notification',name: 'NotificationSettings',component: () => import('@/views/account/settings/Notification'),meta: { title: '新消息通知', hidden: true, keepAlive: true, permission: [ 'user' ] }}]}]}

前端页面显示
在这里插入图片描述


http://chatgpt.dhexx.cn/article/VwqJPk75.shtml

相关文章

Vue学习:路由

2. 路由 2.1 前端路由的发展历程 2.1.1 认识前端路由 路由其实是网络工程中的一个术语&#xff1a; 在架构一个网络时&#xff0c;非常重要的两个设备就是路由器和交换机。 ​ 当然&#xff0c;目前在我们生活中路由器也是越来越被大家所熟知&#xff0c;因为我们生活中都…

vue权限控制和动态路由

思路 登录&#xff1a;当用户填写完账号和密码后向服务端验证是否正确&#xff0c;验证通过之后&#xff0c;服务端会返回一个token&#xff0c;拿到token之后&#xff08;我会将这个token存贮到localStore中&#xff0c;保证刷新页面后能记住用户登录状态&#xff09;&#xf…

Vue 路由权限控制

当我们在做后台管理系统的时候&#xff0c;都会涉及到系统左侧的菜单树如何动态显示的问题。目前基本上都是RBAC的解决方案&#xff0c;即Role-Based Access Control&#xff0c;权限与角色相关联&#xff0c;用户通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了…

【Git CMD】Git常用命令总结

目录 0 git的工作区、暂存区、本地仓库和远程仓库0.1 图解0.2 解析 1 本地仓库1.1 创建版本库1.2 分支1.2.1 查看本地仓库的分支信息1.2.2 创建分支1.2.3 切换分支1.2.4 重命名分支1.2.5 合并分支1.2.6 删除分支 1.3 添加文件到暂存区1.3.1 添加单个文件1.3.2 添加多个文件1.3.…

Git常用命令大全(从入门到使用,学不会评论区骂我)

Git常用命令大全 1&#xff1a;Git全局设置 当安装Git后首先要做的事情是设置用户名称和email地址。这是非常重要的&#xff0c;因为每次Git提交都会使用该用户信息。在Git 命令行中执行下面命令&#xff1a; 设置用户信息 git config --global user.name “你的用户名” …

Git常用命令及方法大全

下面是我整理的常用 Git 命令清单。几个专用名词的译名如下。 Workspace&#xff1a;工作区Index / Stage&#xff1a;暂存区Repository&#xff1a;仓库区&#xff08;或本地仓库&#xff09;Remote&#xff1a;远程仓库 本地分支关联远程&#xff1a;git branch --set-upstre…

Git 常用命令大全

一、 Git 常用命令速查 git branch 查看本地所有分支git status 查看当前状态 git commit 提交 git branch -a 查看所有的分支git branch -r 查看远程所有分支git commit -am "init" 提交并且加注释 git remote add origin git192.168.1.119:ndshowgit push origin …

Git常用命令大全

Git常用命令大全 下面是我整理的常用 Git 命令清单。几个专用名词的译名如下。 Workspace&#xff1a;工作区Index / Stage&#xff1a;暂存区Repository&#xff1a;仓库区&#xff08;或本地仓库&#xff09;Remote&#xff1a;远程仓库 本地分支关联远程 git branch --set-u…

git常用命令总结

1 git概述 1.1 简介 git是分布式版本控制系统&#xff08;Distributed Version Control System&#xff0c;简称DVCS&#xff09;&#xff0c;分为两种仓库 &#xff1a;本地仓库和远程仓库。 本地仓库&#xff1a;是在开发人员自己电脑上的Git仓库远程仓库&#xff1a;是在…

20 个最常用的 Git 命令用法说明及示例

在这篇文章中&#xff0c;我将介绍在使用 Git 时最常使用的 20 个命令。 作者 | Sahiti Kappagantula 译者 | 弯月&#xff0c;责编 | 屠敏 出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09; 以下为译文&#xff1a; 以下是这些Git命令&#xff1a; git config git…

Git基本命令大全

点击上方“小白学视觉”&#xff0c;选择加"星标"或“置顶” 重磅干货&#xff0c;第一时间送达 1、git clone -b <指定分支名> <远程仓库地址> 克隆指定分支 如&#xff1a; git clone -b bestore_master ssh://gitgit-ssh.xxx.com/xxx.git 2、 git bra…

常用git命令总结大全

目录 一、常用命令 1、git init 2、git add 文件名 3、git commit -m “备注” 4、git status 与 git diff 5、git show commit_id 查看某次修改 6、git log 与 git reflow 7、git pull (--rebase) 8、git push (-u) 与 git branch (-u) 9、git reset --hard 与 git…

Git常用命令

这是一篇笔记 //查看某个命令文档 git help <command> git <command> -h git <command> --help1.基本操作 用户配置 git config --global user.name "bettyaner" git config --global user.email bettyaner163.com配置级别 –local&#xff08…

Git 常用命令速查表(收藏大全)

目录 一、新建代码库 二、配置 三、增加/删除/修改文件 四、代码提交 五、分支 六、标签 七、查看信息 八、远程操作 九、撤销 十、其他 名词 master: 默认开发分支 origin: 默认远程版本库 Index / Stage&#xff1a;暂存区 Workspace&#xff1a;工作区 Reposito…

【深度学习】ResNet50

结构 ResNet50结构&#xff1a; 推荐查看&#xff1a;caffe可视化版 resnet50中1x1filter的作用&#xff1a; 1、在shortcut connection block的残差层中使用1x1的fiter先降维&#xff08;channel&#xff09;&#xff0c;然后再使用1x1的fiter升维,使残差层输出与恒等映射…

ResNet-50 结构

ResNet有2个基本的block&#xff0c;一个是Identity Block&#xff0c;输入和输出的dimension是一样的&#xff0c;所以可以串联多个&#xff1b;另外一个基本block是Conv Block&#xff0c;输入和输出的dimension是不一样的&#xff0c;所以不能连续串联&#xff0c;它的作用本…

ResNet 简介

ResNet 本文对resnet进行介绍&#xff0c;文章目录如下&#xff1a; ResNet 历史ResNet 亮点为何层数不能太深residual 残差模块介绍网络结构BN 层迁移学习 本文参考资料有&#xff1a; 6.1 ResNet网络结构&#xff0c;BN以及迁移学习详解 https://www.bilibili.com/video/…

Resnet

再上一偏博文中我们说到越复杂的问题需要越深层的神经网络拟合&#xff0c;但是越深层的神经网络越难训练&#xff0c;原因可能是过拟合以及损失函数的局部最优解过多&#xff08;鞍点过多&#xff1f;导致经过相同的epoch更深的网络的trainerror大于较浅的网络&#xff0c;因为…

ResNet网络详解

ResNet ResNet在2015年由微软实验室提出&#xff0c;斩获当年lmageNet竞赛中分类任务第一名&#xff0c;目标检测第一名。获得coco数据集中目标检测第一名&#xff0c;图像分割第一名。 ResNet亮点 1.超深的网络结构(突破1000层) 2.提出residual模块 3.使用Batch Normalizat…