从壹开始 [vueAdmin后台] 之三 || 动态路由配置 项目快速开发

回顾

今天VS 2019正式发布,实验一波,你安装了么?Blog.Core 预计今天会升级到 Core 3.0 版本。

 

哈喽大家周三好!本来今天呢要写 Id4 了,但是写到了一半,突然有人问到了关于 Blog.Admin 管理后台的一些问题,想着这个前后端系列是第一个项目,而且是以后学习的基础,不能草草了事,所以就把重心往 Blog.Core + Blog.Admin 两个项目上靠拢了下,明天再更新 IdentityServer4 吧,因为从上周末到今天,这几天修改了一些东西,这里就不一一的写文章了,如果你是跟着系列看的小伙伴,应该知道我写的是什么意思,如果是路人,额不敢保证你能看懂我在说什么,总结来说有以下五点:

 

1、Blog.Core 项目增加了单元测试项目 Blog.Core.Tests ,不过就几行代码,做了个小测试而已;

 

2、Blog.Core 项目配置了 AOP Sql 的功能,就是大家在操作仓储的时候,会生成指定的Sql语句,保存到日志中,方便查看,这是SqlSugar的核心功能;

 

 

3、Blog.Core 项目增加多线程日志功能,防止出现死锁,可以查看 BlogLogAOP.cs ,当然你可以封装起来;

 具体查看方法:static void OutSql2Log(string dataIntercept)

 

4、DDD开发之 Christ3D 项目完成了末尾开发,实现了 Identity 登录(注意不是 Id4 ),并完成部署

地址:http://123.206.33.109:4773

 

5、Blog.Admin 项目实现数据库配置动态路由,这个是今天的重点内容,其他的都很简单,大家看看就行。

然后也简单说说,如何在 Blog.Admin 项目中,快速添加页面,虽然这个真的很简单。

 

好啦,马上开始今天的 Share Time。

因为这个系列我还没有写过一些文章,所以今天就把两种写法都写上,既补充了之前的方案,又设计新的方案。

 

一、传统的权限路由是如何设计的?

首先先来个动图(注意这个方案已经弃用了,看看个过程即可,下边第二节有个动图,才是以后开发的模板):

 

 

 

1、设计页面

 这个步骤很简单,也很普通,我们开发项目,肯定需要设计页面了,这个不属于权限路由的一部分,

不过要说的就是,这个页面路径的设计,要考虑清楚,有的小伙伴,习惯一股脑全部并列放页面,也有详情页用 detail.vue 或者 id.vue ,还有的是 _id.vue 的,具体的这些规则,需要好好想想,设计清楚,不细说。

 

2、路由实例配置

 ( routerManuaConfig.js 文件 )。相信这个路由大家都已经很明白了,只要是写过 vue 的,有一点点基础的,项目初期,我们每次开发页面,如果需要在项目中使用,就必须将路由实例添加到 Vue 实例里,其实说白了,就是一个对象,这样 Vue 实例才能调用,也就是能通过 url 访问到我们的页面。这个其实是很合理的,也是很正确的,官方也是这么处理的,比如我的 Blog.Admin 项目中,就是这么配置的:

 

可是,嗯,就怕可是,现在我的页面大概有十多个,就已经一大串了,有时候修改一个路由,需要找半天,这个设计貌似不是随着项目的扩大,越来越不合理,我见过一个项目,页面有50+,那配置了一大大大页的路由 json,嗯,感觉会有解决方案的,没错,vue 官方也说提到了,具体的,下文会详细说明,咱们先安装这个方案进行下去。

那既然是权限路由,路由有了,就该权限了。

 

3、配置权限菜单(路由)

Login.vue 、App.vue 文件)虽然我已经覆盖了 GitHub 上的代码,但是这个方案的写法还保留着,大家如果真的就喜欢这种配置,也可以看看。

 上边我们是把所有的路由都注入到了 Router 实例里,那如何渲染权限菜单呢,没错,思路很简单:

1、当前用户登录;

2、获取到用户标识,比如 Id ;

3、根据用户标识获取权限菜单的 Json 数据;

4、根据 Json  数据,渲染到左侧菜单上。//查看 App.vue 文件中的 routes 这个数组。

 其实前三步都是在 Login.vue 页面内进行的,很简单的两个方法 GetNavigationBar() ,自己看看即可。

 

4、菜单表中,添加新建的页面菜单

上边的步骤中,仅仅是将之前的页面渲染出来了,如果我们新建了一个页面,新的页面还没有显示出来,

所以就需要在后台管理 -> 菜单权限管理 -> 菜单管理中,新建一条数据:

 

5、角色与菜单的分配

上边菜单数据配置好后 ,就需要对当前用户所对应的角色,进行权限的再次分配了,这个很好理解,这个是核心,前边所以的操作都是给这个做铺垫的:

 

6、存在的小问题

其实关于权限路由,上边五步走已经实现了,是不是感觉很简单(这里只说菜单,不说API权限问题,这个是上一篇的《二 || 完美实现 JWT 滑动授权刷新》已经说到了),但是现在面临着两个问题:

1、就是上边提到的,需要将很多的路由一一的配置好,注入到路由实例;

2、因为这样是一次性把全部的路由都注入,如果当前用户没有这个页面的权限,虽然左侧的菜单看不到这个页面,但是如果他强行访问这个url的话,还是会出现的;

当然我们设计了 api 权限,他看不到内容,但是再强行访问这个URL的时候,还是会在当前页面停留,并看到页面骨架(就是没有数据,但是有按钮啥的),

当然我们可以让用户直接跳转到403页面,嗯,也是一个办法。

 那有没有什么办法,可以动态的生成路由实例呢?欸,要是有这个想法,就是人才,请往下看。

 

二、动态生成路由实例


 上边我们研究了一般实现权限路由菜单的方法,很简单,很直观,但是有两个小问题,其实对我来说,主要的还是手动配置路由的问题,页面现在的太多,每次开发一个页面,不仅需要添加路由,还需要在菜单表里,增加该菜单,然后对权限勾选该菜单,从这个逻辑来看,好像在vue项目中,配置路由就成了冗余的一步了,那如果不配置了,我们该怎么办呢?这就是今天要说的重头戏 —— 动态路由实例。

 

首先再来个动图,以后就用这个方法进行快速开发了:

 

 

 上边的这个已经对当前路由页面做了权限匹配,我们匹配好了页面,剩下的就是开发 API 接口了,开发成功后,要注意两点:

1、增加接口的授权特性;

 

2、后台对路由进行编辑,增加api接口;

 

 

1、在路由实例中注入项目基础路由

 这里要说的重点是 基础路由 ,那什么是基础路由呢?就是我们在项目启动的时候首次运行的页面(比如登录页),或者不需要参与数据库权限配置的某些路由(比如欢迎页,404页),比如我将基本的路由实例重新整理如下:

在项目的 src 文件夹下,新建一个 router 文件夹,然后新建主程序文件 —— index.js ,这个文件以后就是我们的新的路由方案,用来替换之前的 router.js(现在更名为 routerManuaConfig.js

 

 大家可以看到,现在除了这几个基础路由,其他的都被删除了,统一通过动态注入的方式添加。

 

2、根据环境配置导入组件文件

 这里其实是一个小坑,以前没有研究过,后来搜索了下,才知道的,原来 vue 动态导入 vue 文件,在不同的环境还不一样,所以这里特别用了一节来说明下,虽然内容很简单:

还是在 src/router 文件夹下,建立两个文件,然后定义导入方法:

 

_import_development.js
//开发环境导入组件
module.exports = file => require('@/views' + file + '.vue').default_import_production.js
//生产环境导入组件
module.exports = file => () => import('@/views' + file + '.vue')

 

那如何进行导入呢,就是下边的重头戏了。

 

3、动态生成权限路由(核心) 

这个具体的code 在 src 的根目录下的 promissionRouter.js 文件里,

A、组件导入 —— _import

这个其实很简单,只需要根据当前的环境变量,获取指定的导入方案,传入路径地址作为参数,就可以很好的实现当然地址的注入。

//获取组件的方法
const _import = require('./router/_import_' + process.env.NODE_ENV)// .......//导入路径下的组件
route.component = _import(route.path)

 

B、获取数据与保存钩子 —— router.beforeEach

 这个是一个核心,就是我们每次在路由切换的时候,都需要动态处理路由实例,这里还有点儿瑕疵,我会在以后慢慢完善,但是思路就是这样的,这里的路由数据来自两个方面,一个是api接口获取,一个是将获取到的数据存放在本地:

var storeTemp = store;
router.beforeEach((to, from, next) => {//动态添加路由
    {//不加这个判断,路由会陷入死循环if (!getRouter) {if (!getObjArr('router')) {var user = window.localStorage.user ? JSON.parse(window.localStorage.user) : null;if (user && user.uID > 0) {var loginParams = {uid: user.uID};getNavigationBar(loginParams).then(data => {console.log('router before each get navigation bar from api succeed!')if (data.success) {getRouter = data.response.children//后台拿到路由saveObjArr('router', getRouter) //存储路由到localStoragerouterGo(to, next)//执行路由跳转方法
                        }});}} else {//从localStorage拿到了路由getRouter = getObjArr('router')//拿到路由
                routerGo(to, next)}} else {console.log(to)if(to.name&&to.name != 'login'){getRouter = getObjArr('router')//拿到路由global.antRouter = getRouterrouterGo(to, next)//执行路由跳转方法
           }next()}}
});

具体的写法都很简单,就是判断和保存到路由,不细说了,大家pull 下代码,看看即可。

 

 

C、路由过滤 ——  filterAsyncRouter

 这里要说下,这个是关键,因为我们获取到的,仅仅是一个json格式的权限列表,我们的路径还是一个字符串 url ,但是我们的动态路由,需要一个 component ,也就是刚刚我们获取到的对应组件,所以,我们需要来一个过滤器,将获取到的json数据中,每一个菜单,都添加进去一个 component 组件信息。

//遍历后台传来的路由字符串,转换为组件对象
function filterAsyncRouter(asyncRouterMap) {//注意这里的 asyncRouterMap 是一个数组const accessedRouters = asyncRouterMap.filter(route => {if (route.path) {if (route.path === '/') {//Layout组件特殊处理route.component = Layout} else { route.component = _import(route.path)}}if (route.children && route.children.length) {route.children = filterAsyncRouter(route.children)}return true})return accessedRouters
}

 

 

D、生成路由实例与运行 —— routerGo

 上边所有的工程中,我们把路由已经生成好了,就剩下最后一步注入到实例里,没错,就是红色的部分,是不是很简单:

function routerGo(to, next) {getRouter = filterAsyncRouter(getRouter) //过滤路由router.addRoutes(getRouter) //动态添加路由global.antRouter = getRouter //将路由数据传递给全局变量,做侧边栏菜单渲染工作next({...to, replace: true})
}

 

4、遗留问题——重登新用户路由不同步

这个是个很搞笑的问题,怎么说搞笑呢,好像上边的 addRoutes 这个方法设计之初,没有设计过动态删除?额好吧,我就是开个玩笑,对尤大大的框架,还是很给力的。

现在有一个问题,就是我现在是test账号,然后没有 测试管理 这一组,这个期间我刷新了页面,然后当我切换超级管理员 blogadmin 的时候,虽然有这个左侧菜单,但是点击测试页面1的时候,提示404,证明啥,证明这个路由虽然渲染出来了,但是没有导入到路由实例里,路由对象里,还是之前的,这个时候,必须刷新一下才能正常访问,请看:

 

 

这个时候我研究出来两个方案,虽然两个在本地都可以,但是第二种在发布模式下不行,也就是dist 上传到服务器就不行了,这里做下记录,暂时使用第一种方案,可能你遇不到。

1、在系统登出,也就是退出登录的时候,刷新页面,使用 window.location.reload(),在 App.vue 的logout 方法里;

2、使用 router.matcher 来替换,在 router/index.js 下的 resetRouter方法,和调用处:promissionRouter.js 下的 routerGo 方法里调用;

目前一切正常,就暂时使用刷新页面的第一种方案,第二种以后慢慢研究。

您可参考这个issues

 

三、支持多级菜单——递归

这个其实很简单,以前我的版本只是支持两级菜单,那如果想实现无限级别的多级菜单的话,就肯定需要用到递归的概念了,简单来说,就是——递归组件。

具体的原理相信有一点基础的小伙伴都能理解,就是写一个递归组件,将路由菜单 JSON 给递归渲染出来即可:

1、设计菜单递归组件

在 src -> components -> 下,新建 Sidebar.vue 组件,实现自身嵌套递归:

<template><div><template v-if="item.children"><el-submenu :index="item.id+'index'" v-if="!item.leaf"><template slot="title"><i class="fa" :class="item.iconCls"></i><span class="title-name" slot="title">{{item.name}}</span></template><template v-for="child in item.children"><!-- 这里实现自己递归嵌套 注意这个名称 --><sidebarv-if="child.children&&child.children.length>0":item="child":key="child.path+'5'"/><el-menu-item v-else :key="child.path+'5'" :index="child.path+'5'"><i v-if="child.children&&child.children.length>0" :class="item.iconCls"></i>{{child.name}}</el-menu-item></template></el-submenu></template>  <!-- 没有子节点,直接输出 --><template v-else><el-menu-item :index="item.path"><i class="fa" :class="item.iconCls"></i><template slot="title"><span class="title-name" slot="title">{{item.name}}</span></template></el-menu-item></template></div>
</template><script>export default {name: 'Sidebar',props: {item: {type: Object,required: true}}}
</script>

 

2、在App.vue 中进行调用

当然,你也可以放到模板组件,也就是 Layout.vue 中调用,把左侧菜单,页头,页脚放到 Lauout 布局页,不过我都放到了 App.vue 了:

 <el-menu  :default-active="$route.path"class="el-menu-vertical-demo" @open="handleopen" @close="handleclose" @select="handleselect"unique-opened router :collapse="isCollapse"background-color="#2f3e52"text-color="#fff"active-text-color="#ffd04b"><sidebar v-for="(menu,index) in routes" :key="index" :item="menu" /></el-menu>

 

记得导入组件:

 

来看看最终的效果吧:

 

 

 

四、结语

 好啦,今天的文章,就先写到这里吧,最近比较累心,感觉努力付出和收获不是正比,虽然还在学习,还是想要歇一歇了。

今天的所有内容都已经提交到 Github,然后在线 demo 也做了改变,大家可以自行查看。

 

五、Github && Gitee

 

.NET CORE 源码:

Github:  https://github.com/anjoy8/Blog.Core

Gitee :   https://gitee.com/laozhangIsPhi/Blog.Core

 

VUE 项目开源代码:

https://github.com/anjoy8/Blog.Vue

 

--- ♥♥♥ ---

转载于:https://www.cnblogs.com/laozhang-is-phi/p/10643993.html

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

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

相关文章

c语言支持默认参数吗,嵌入式C语言可以带“默认参数”的函数吗

描述(文章来源&#xff1a;嵌入式时代)使用C开发过程序时&#xff0c;定义函数可以指定默认参数&#xff0c;例如 void fun(int x, int y3); 在调用 fun() 时第二个参数可以不传递&#xff0c;此时 fun() 函数默认第二个参数等于 3&#xff0c;例如 f(1) 就相当于 f(1,3)。这是…

《从零开始学习ASP.NET MVC 1.0》-开天辟地入门篇

《从零开始学习ASP.NET MVC 1.0》 文章导航 (一) 开天辟地入门篇 (二) 识别URL的Routing组件 (三) Controller/Action 深入解析与应用实例 (四) View/Model 全解 (五) ViewEngine 深入解析与应用实例 一.摘要 随着ASP.NET MVC 1.0版本的正式发布, 我将本系列文章也更新到了1.0,…

制作放两个小图片的按钮 - 回复 xhui 的问题

为什么80%的码农都做不了架构师&#xff1f;>>> 问题来源: http://www.cnblogs.com/del/archive/2009/03/12/1409708.html#1475240 本例效果图: 自定义的类(TMyButton): unit Unit2;interfaceusesWindows, Messages, Classes, Graphics, StdCtrls;typeTMyButton c…

c语言 指针 pdf,深入理解c指针 PDF扫描版[33MB]

深入理解C指针 内容简介&#xff1a;深入理解C指针和内存管理&#xff0c;提升编程效率&#xff01;这是一本实战型图书&#xff0c;通过它&#xff0c;读者可以掌握指针动态操控内存的机制、对数据结构的增强支持&#xff0c;以及访问硬件等技术。本书详细阐述了如何在数组、字…

使用SQL Server 2008提供的表分区向导

表分区&#xff08;Partition Table)是自从SQL Server 2005就开始提供的功能&#xff0c;解决的问题是大型表的存储和查询。 我们之前大致的语法是这样的 -- -- 演示&#xff1a;陈希章 -- 如何创建分区函数 -- 如何创建分区架构 -- 如何创建分区表 -- alter database adventu…

唤醒控件曾经拥有的能力

为什么80%的码农都做不了架构师&#xff1f;>>> 控件的祖先 TControl 有很多功能, 但它的有些子孙确丧失了很多(为了专用). 譬如 TBevel 就没有把一些常规的事件继承下来, 那些没有被继承的功能一般都隐藏在 protected 区, 如果重新继承是可以使用的. 这里尝试了另…

c语言掌上通,计算机二级C语言掌上通在哪下载安装?计算机掌上通好用吗?

计算机二级C语言掌上通在哪下载安装&#xff1f;计算机二级C语言掌上通好用吗&#xff1f;相信很多想要考计算机二级C语言的学生都在努力的复习背题库&#xff0c;而现在只要使用计算机二级C语言掌上通就能够随时随地刷题了。一、计算机二级C语言掌上通怎么下载1、滑动手机&…

[原]RHCS集群的服务切换测试札记

估计使用Red Hat或者CentOS做HA集群的朋友多数都会选择RedHat Cluster Suite&#xff08;RHCS&#xff09;这个套件来做吧。本篇主要记录构建及测试时候的情况。 poweroff 和 reboot 这种常规操作的服务切换取决于 recovery"relocate" 这个参数&#xff0c;在图形化界…

理解 Delphi 的类(十) - 深入方法[17] - 提前声明

为什么80%的码农都做不了架构师&#xff1f;>>> //要点17: 如果前面的方法要调用后面的方法, 后面的方法需要提前声明 function MyFunB(x: Integer): Integer; forward; {使用 forward 指示字提前声明}function MyFunA(x: Integer): Integer; beginResult : MyFunB…

本地仓库推送到远程仓库:fatal: refusing to merge unrelated histories

最近&#xff0c;在操作git的时候&#xff0c;遇到各种问题&#xff0c;下面总结一下。 最开始&#xff0c;我不是先把远程仓库拉取到本地 &#xff0c;而是直接在本地先创建一个仓库&#xff0c;再git remote add添加远程仓库。 当然&#xff0c;gitee官方还是有操作指南&…

C#开发微信门户及应用(18)-微信企业号的通讯录管理开发之成员管理

在上篇随笔《C#开发微信门户及应用(17)-微信企业号的通讯录管理开发之部门管理》介绍了通讯录的部门的相关操作管理&#xff0c;通讯录管理包括部门管理、成员管理、标签管理三个部分&#xff0c;本篇主要介绍成员的管理操作&#xff0c;包括创建、删除、更新、获取、获取部门成…

转载--【笔记】Asp.Net构架(Http请求处理流程)

转载--【笔记】Asp.Net构架(Http请求处理流程) 最近接触了一些Asp.Net开发人员&#xff0c;在聊到技术的时候&#xff0c;发现很多的Asp.Net开发人员都不明白在ASP.NET中一个页面请求的处理流程&#xff0c;今天自己在反思的过程中&#xff0c;发现我自己对这个也是一知半解&am…

SSL/TLS协议运行机制的概述

互联网的通信安全&#xff0c;建立在SSL/TLS协议之上。 本文简要介绍SSL/TLS协议的运行机制。文章的重点是设计思想和运行过程&#xff0c;不涉及具体的实现细节。如果想了解这方面的内容&#xff0c;请参阅RFC文档。 一、作用 不使用SSL/TLS的HTTP通信&#xff0c;就是不加密的…

margin塌陷

一&#xff1a;什么是margin塌陷 在标准文档流中&#xff0c;竖直方向的margin会出现叠加现象&#xff08;水平方向不会塌陷&#xff09;&#xff0c;两个margin紧挨着&#xff0c;中间没有border或者paddingmargin直接接触&#xff0c;就产生了合并表现为较大的margin会覆盖掉…

Redis链表实现

链表在 Redis 中的应用非常广泛&#xff0c; 比如列表键的底层实现之一就是链表&#xff1a; 当一个列表键包含了数量比较多的元素&#xff0c; 又或者列表中包含的元素都是比较长的字符串时&#xff0c; Redis 就会使用链表作为列表键的底层实现。除了链表键之外&#xff0c; …

linux optimizer,linux命令1—安装optimizer(示例代码)

ZendChina官方&#xff1a;下面介绍一下关于在linux环境下Zend Optimizer 3.3的安装方法。本篇文章是基于RHEL5架构的linux系统。(1)ZendOptimizer 3.3.3版本的安装。ZendOptimizer 3.3.3下载地址&#xff1a;安装&#xff1a;# wget http://down1.chinaunix.net/distfiles/Zen…

LINQ to SharePoint 试用感受, 欢迎讨论~

目前项目需要比较复杂的查询功能&#xff0c; 如果直接写CAML的话以后维护非常麻烦&#xff0c; 然后暂时用LINQ to SharePoint替代之&#xff5e; URL&#xff1a; http://linqtosharepoint.codeplex.com/ 最大的优点是我不用写CAML了&#xff0c; 非常容易实现一些简单的查询…

ps4移植android游戏,把PS4游戏《Apex英雄》《只狼》搬到安卓手机上玩,这招够简单!...

近期&#xff0c;索尼抢先其他平台&#xff0c;在PlayStaion官方网站发布新信息&#xff0c;使各大主流射击游戏平均流失率达到57%的大逃杀游戏《Apex英雄》&#xff0c;本赛季会加入两名新角色&#xff0c;一位是刚更新的辛烷&#xff0c;另一位会在本赛季结束前推出。根据此前…

VC系统扫雷游戏外挂源代码程序下载(转帖

VC系统扫雷游戏外挂源代码程序下载&#xff08;转帖&#xff09;2008-03-04 10:25经过了多次测试写出了历史上第一个有点意义的MFC程序。效果差强人意。^_^ CODE:// CrackWinmineDlg.cpp : implementation file// #include "stdafx.h"#include "CrackWinmine.h&…

JAVA面向对象程序设计(第二版) 袁绍欣 第四章答案

面向对象&#xff08;上&#xff09; 1.名词解释&#xff1a;构造方法、抽象 构造方法一般是用来初始化数据成员的&#xff0c;与类名相同&#xff0c;无返回值。 分为有参和无参数&#xff0c;当一个类中没有定义构造函数时&#xff0c;系统会给该类中加一个默认的空参数的构造…