从壹开始 [Admin] 之五 ║ 实现『按钮』级别权限配置

正文

一、前情回顾

哈喽大家好,在这个欢庆的日子里,老张祝大家工作都能蒸蒸日上!今天正好也是社团成立的第一天,我也是希望今天能是个纪念日,沾沾这个大喜庆!

放假这两天,倒是学到了很多东西,我这个也是承认的,昨天的事务提交,今天的按钮级别的权限,都是群里小伙伴提供的方案和思路,我就是诚惶诚恐的写到文章里了,我总怕会说我是知识的偷盗者,当然我这个完全是为了社区,我毕竟一分钱没有得到,无论访问量有多少,可能充其量就是数字好看。

 

言归正传,还记得半年前(2019.02.27)的时候,我的 vue 项目之二:Blog.Admin 正式开源(https://github.com/anjoy8/Blog.Admin),当时打算做一个简单的权限后台系统,我自己想了常用的一些功能,当然有人说丑,有人说乱,但是也有人在自己项目和公司中使用,不过也是我付出心血的,而且也是完美的配合了 Blog.Core 项目,当时几大设想功能中,迟迟有一个功能没有实现,搁置了很久 —— 就是按钮级别的权限配置:

 

640?wx_fmt=png

 

 

 

当时我为啥没有做这个呢,有两点考虑,1、是因为超级管理员我没让大家访问,就怕误操作数据,对别人观看权限有影响;2、另一个考虑,就是想把按钮暴漏出来,看看是不是真的 test 测试账号能不能删除数据。后来我就开始思考,是时候把这个权限加进来了,就是没有删除的权限,删除按钮就不显示,但是考虑了很久,被一个小知识点给卡住了,就是没有想到如何动态事件绑定,这个不懂没关系,我下文章会说到,前天由群管理  @大黄瓜和@Kawhi 提供了解决思路和方案,眼前一亮,终于实现了这个功能。

投稿作者:@大黄瓜 and @Kawhi;

效果预览:我为了防止大改,目前只在 “角色管理” 页,增加了这个功能,正常后,全部替换;

在线地址:http://vueadmin.neters.club

Github分支:主分支;

 

 下边就开始正式讲解,分成了两部分,步骤+重点知识说明,所以看步骤的时候,直接动手操作就行了,不用管为什么,下边的第三部分——重点知识的说明,会简单说说。

 

二、详细设计步骤

1、后端微调,保存按钮相关信息

不知道还有没有小伙伴记得,我现在后台的权限系统中,左侧的导航条已经自动化了,所谓的自动化,就是已经完全交给了数据库,无论增加多少权限,不用前端或者后端进行操作,只需要配置即可达到目的,当时呢,我把左侧的菜单和按钮揉到了一张表里,当时感觉很不合理,但是现在又改起来简单,得益于这个设计思路,所以这次我们几乎不用改什么,只需要把按钮信息给放出来即可,这里有两个小点:

1、Permission.cs 菜单表中,新建字段 Func ,用来存放当前按钮所对应的方法事件;

2、/Permission/GetNavigationBar 接口中,把 IsButton==false 限制去掉,使之可以配合菜单进行递归;

//var rolePermissionMoudles = (await _permissionServices.Query(d => pids.Contains(d.Id) && d.IsButton == false)).OrderBy(c => c.OrderSort);
var rolePermissionMoudles = (await _permissionServices.Query(d => pids.Contains(d.Id))).OrderBy(c => c.OrderSort);

3、在 RecursionHelper.cs 中,增加 IsButton 属性,将数据库数据,拼车 Tree 返回到前端;

这样我们就把按钮数据配合着菜单数据一起返回前端了,你可以来查看下:

640?wx_fmt=png

 

 

 

到这里,我们第一部分——后端数据就完成了,当然,如果你想更炫酷,可以多增加字段,比如按钮的样式,或者其他属性等等等,这里你肯定明白,我就不细说了。

从下边开始,我们就开始说 Blog.Admin 项目了,请打开 VSCode ,来修改我们的 Vue 项目:

 

2、修改后台权限管理,添加按钮事件

这个步骤很简单,就是把上边我们建立的那个 Func 字段,给在页面里增删改查一下就好了,具体的代码自行修改即可。

 

640?wx_fmt=png

 

 

 

3、控制“按钮”不要和“菜单”展示冲突

刚刚我们上边说到了,把按钮数据配合着菜单一起开放了出来,那这个时候我们要需要检查一下,不能和菜单的展示起冲突,这里我就直接说修改的地方了:

 

1、修改 Sidebar.vue 组件,让按钮的数据不要进行展示,具体的看看代码就明白了,很简单;

2、修改 src\router\index.js 中的动态路由注入方法,过滤掉按钮数据;

640?wx_fmt=png

 

 

到了这里,我们的第二部分——准备工作就做完了,接下来,就是本文的重中之重的重头戏,设计这个工具栏了,那具体怎么操作,这个时候我希望你可以先暂停一下,先不要往下看,先自己脑中考虑一下,按照我的思路,就是按钮数据也已经有了,如何设计这个公共组件呢?考虑五分钟吧......

 

五分钟后,假设你已经考虑过了,那我就开始正式说明。

 

4、设计工具栏组件——Toolbar.vue

 

既然要做成自动化的组件,就一定要抽象出来,那第一步就是建立一个组件,不能每个页面都写相似的一堆代码;

其实呢,我们也要可以配置,不能仅仅把按钮给提出来,还应该有其他的比如<input />搜索框等等,都应该放到工具栏里;

一定要加载或者不加载,不能show or hide,这样别人也会在查看元素的时候,看到;

 

综上所述 ,我的设计是把表格里的按钮,全部提到了顶部,先给大家一个展示的效果图,这个删除颜色是我手动加的,你也可以自己加个字段配置:

640?wx_fmt=png

 

 

 

首先我们创建组件,src\components\Toolbar.vue ,具体的代码如下:

640?wx_fmt=gif

<template>  <el-col v-if="buttonList.length>0" :span="24" class="toolbar" style="padding-bottom: 0px;">    <el-form :inline="true" @submit.native.prevent>      <el-form-item>        <el-input v-model="searchVal" placeholder="请输入内容"></el-input>      </el-form-item>      <!-- 这个就是当前页面内,所有的btn列表 -->      <el-form-item v-for="item in buttonList">        <!-- 这里触发点击事件 -->        <el-button type="primary" @click="callFunc(item)">{{item.name}}</el-button>      </el-form-item>    </el-form>  </el-col></template><script>export default {  name: "Toolbar",  data() {    return {      searchVal: "" //双向绑定搜索内容    };  },  props: ["buttonList"], //接受父组件传值  methods: {    callFunc(item) {      item.search = this.searchVal;      this.$emit("callFunction", item); //将值传给父组件    }  }};</script>

640?wx_fmt=gif

 

相信每个人都能看的懂,只是字面意思能看得懂,其中的核心知识点就是 List for渲染,父给子传值,子给父传值,我下文会重点讲到,其中 buttonList  数组的格式,很简单,你可以自己后端封装一下,我这里就偷懒了,直接使用的菜单的数据结果,就是上边我 localstorage.routes 中的结构,毕竟我把按钮和菜单共有一套嘛。

那现在我们设计好了子组件——工具栏,接下来就要设计父组件了,传递数据和接受子组件广播了。

 

 

5、将按钮事件绑定到组件上

刚刚我们说到了 ,在 Toolbar.vue 中,核心的内容,就是把动态的事件方法给推送到一个个父组件上,这里是以 Role.vue 页面举例的,所有用到了  $emit("callFunction", item)  方法,这个如果你开发vue的话,肯定都知道这个的,这个父子通讯实例中,使用很多,具体的我在之前的文中中,也讲到了,你可以看看,这里不细说,说白了一句话,就是子组件执行父组件方法。二十║Vue基础终篇:传值+组件+项目说明。其实到这个地方,我也想到了,但是问题来了:你可以先看看 emit 的用法,使用 emit 一般都是传递数据,但是如果传递 function 的话,肯定也是一个 name 的字符串,那父组件接受到这个 function name 的时候,很容易当成一个 data,如果强行执行,他们又不在一个对象里,因为有闭包,如何让页面执行这个 function 呢?我思考了很久(说明自己学的不到家)。


这个就是这两个月来困扰我的地方,前边的思路和后边的 Table 隔离我都想到了,只是这里我没有想到,看来还是需要一些高级前端的朋友哟,前天听到了一个 apply 方法后,我豁然开朗,原来可以这样,那下边我就详细的说一说,如何父组件执行事件:

在 src\views\User\Roles.vue 页面呢,修改我们的工具栏使用:

640?wx_fmt=png

 

 

 

这种引用组件,在data中,定义 buttonList ,就不说,重点还是要理解 @callFunction 这个必须要和子组件的 $emit 中的方法名一致。然后我们定义 callFunction,用来动态执行一个个事件:

640?wx_fmt=gif

   
 callFunction(item) {//这个 item 就是我们的 permission.cs 数据      this.filters = {        name: item.search      };//这里是把子组件中的 search 内容,也接受过来      this[item.Func].apply(this, item);//核心就是要执行 apply 方法    },

640?wx_fmt=gif

 

是不是很简单,难点就在于,.apply()这个方法,下文会说到。这个 this ,就是当然父组件的内容,就是我们执行可以在子组件来调用父组件的方法了

640?wx_fmt=png

 

 

 

这里再说下

 

6、父组件获取 ButtonList 数据

上边我们也说到了,我们把 button 和 菜单揉在一起了,所以我们很简单操作一下之前的数据就行,做一下筛选:

640?wx_fmt=gif

// 在 mounted 钩子中,调用 router   let routers = window.localStorage.router      ? JSON.parse(window.localStorage.router)      : [];    this.getButtonList(routers);
// 定义方法,目的我为了递归 getButtonList(routers) { let _this = this; routers.forEach(element => { let path = this.$route.path.toLowerCase(); if (element.path && element.path.toLowerCase() == path) { _this.buttonList = element.children; return; } else if (element.children) { _this.getButtonList(element.children); } }); }

640?wx_fmt=gif

 

OK,数据准备完毕。

 

7、修改 Table 组件,将工具栏与 Table 逻辑隔离

到了这里就是最后一步了,我们把之前的 tabel 右侧 “操作栏” 删掉,统一放到顶部,然后绑定数据,就可以加载出来了,

640?wx_fmt=png

 

 

 

现在我们把操作栏给取消了,但是我们如何获取 scope.row  呢?是不是很麻烦,要修改很多呢,其实不是的。

 

8、Table 改为单选,通过点击,选择某行

这个功能特别简单,思路就是通过单击某一行,来获取这个 table 的 row,这个 element 官网写的很详细,我就简单的说一下吧:

640?wx_fmt=gif

//触发事件,获取到这个row    selectCurrentRow(val) {      this.currentRow = val;    },

<!--列表--> <el-table :data="users" highlight-current-row v-loading="listLoading" @current-change="selectCurrentRow" style="width: 100%;" >

640?wx_fmt=gif

 

然后只需要简单的修改一下我们的 edit 和 delete 方法即可,因为我们已经拿到了这个 row:

640?wx_fmt=png

 

 

 

 搞定啦!是不是很简单,几乎没有修改什么,感觉之前设计的方案还可以吧,至少扩展还是很不错的!

到了这里,我们的动态按钮权限功能,就已经完全做完了,一个八个步骤,大家动手起来,搞一搞吧。

 

三、重点知识解析

 

1、组件 ——子传父 & 父传子

 这块内容呢,其实我们都已经讲过很多遍了,父传子很简单,只需要定一个自定属性即可,然后子组件接受,比如上文中的:

640?wx_fmt=gif

  <toolbar :buttonList="buttonList" @callFunction="callFunction"></toolbar>
name: "Toolbar", data() { return { searchVal: "" //双向绑定搜索内容 }; }, props: ["buttonList"], //接受父组件传值

640?wx_fmt=gif

 

比较复杂的就是 子传父 了,重点还是要了解一些 $emit 这个api,二十║Vue基础终篇:传值+组件+项目说明 我这篇文章写的还算是详细,如果还是不懂,咱们再一对一讨论吧。

 

 

2、动态事件绑定—— apply

 这个apply 有点儿想 call 回调函数,首先,每个函数都包含两个非继承而来的方法:.apply()和 .call()。这两个方法的用途都是在特定的作用域中调用函数,实际等于设置函数体内this对象的值。

这两个方法接收的参数可以分为两个部分,

  第一部分是在其中运行函数的作用域,如果就在当前函数体中运行,就可以直接使用this值,如果在window作用域中使用,可以传入window值,这样,可以实现扩充作用域;

  第二部分是参数组,在apply中可以传入Array实例,也可以是arguments对象;在call中,传递给函数的参数必须逐个列举;如果没有参数,这个部分可以省略。

首先我们来看看网上apply()方法的定义:

1. apply()方法能劫持另外一个对象的方法,继承另外一个对象的属性

2.Function.apply(obj,args)方法能接收两个参数

3.obj:这个对象将代替Function类里this对象

4.args:这个是数组,它将作为参数传给Function(args–>arguments)

 


举个例子,如下所示:

640?wx_fmt=gif

function sum(num1,num2){  return num1+num2;}
//两个数相等就相加,不相等就相乘function mul(num1,num2){ if(num1 != num2){ return num1*num2; }else{ return sum.apply(this,arguments); //可以为 sum.apply(this,[num1,num2])或sum.call(this,num1,num2); }}
console.log(mul(5,6)); //30console.log(mul(6,6)); //12

说句简单的,我认为就是在其他地方,去调用某一个方法,很重要的一个点,就是 this 这个到底指向什么,自己可以好好调调。

 

3、动态路由过滤—— addRoutes

 这个在上边的步骤里我没有说到,是因为我们把 按钮 给放出来以后,在动态菜单路由的时候,会出现重复的问题,所以我们就需要坐下过滤,注意这个不是错误,是警告,意思就是我们把一些重复的东西添加到路由里了,路由会忽略掉,只不过给大家一个 warm 而已。

640?wx_fmt=png

 

所以呢,我做了一个过滤,封装了下 route.addRoutes——在 src\router\index.js 中,我们过滤下重复路由,还是递归:

640?wx_fmt=gif

router.$addRoutes = (params) => {
var f = item => { if (item['children']) { item['children'] = item['children'].filter(f); return true; } else if (item['IsButton']) { return item['IsButton']===false; } else { return false; } } var paramsFilt = params.filter(f);
router.addRoutes(paramsFilt)}

然后咋其他的地方,将 router.addRoutes 统一都换成 router.$addRoutes 。搞定 

 

四、Github && Gitee

 

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

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

 

一起学习,一起进步 QQ群:867095512

640?wx_fmt=jpeg


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

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

相关文章

MySQL——binlog,redo log

一、什么是binlog、redo log binlog属于逻辑日志&#xff0c;是逻辑操作。innodb redo属于物理日志&#xff0c;是物理变更。逻辑日志有个缺点是难以并行&#xff0c;而物理日志可以比较好的并行操作。 binlog是MySQL Server层记录的日志&#xff0c; redo log是InnoDB存储引…

“自启动”树莓派上的 .NET Core 3.0 环境

点击上方蓝字关注“汪宇杰博客”导语昨天发了一篇《自动配置环境变量Rapbian 系统启动时会去执行 .profile 文件里的命令。因此我们只需要把配置环境变量的命令加入 .profile 文件即可。在 Linux 中&#xff0c;.profile 扩展用于终端程序中的文件。Linux 和 Mac OS X 终端程序…

Nginx 配置内网访问树莓派4 ASP.NET Core 3.0 网站

喜迎国庆点击上方蓝字关注“汪宇杰博客”导语前几天发了两篇《能跑就行&#xff1a;Kestrel Hosting如果你的要求只是临时内网访问&#xff0c;可以只用 Kestrel 来承载 Web 服务器&#xff0c;只需要给 dotnet 命令一个 --urls 参数即可设置允许访问的主机名和端口号。我不希望…

常见的NoSQL数据库

NoSQL数据库发展迅猛&#xff0c;据说现在已经有上百种NoSQL数据库了&#xff0c;下面来了解下常见的一些NoSQL数据库 先来看张表&#xff0c;了解下典型的NoSQL数据库的分类 NoSQL一般特征&#xff1a; 临时性键值存储一般作为关系型数据库的缓存来使用由于存在数据丢失的可…

2019年9月中国编程语言排行榜,C#排第三

前两天&#xff0c;在 GitHub 上看到一份报告&#xff0c;上面统计了全国程编程语言排行榜 9 月份的情况&#xff0c;下面跟大家分享一下&#xff0c;这份报告里面都说了什么。https://github.com/juwikuang/china_job_survey/blob/master/notebook/201909/programming_languag…

从0开始编写dapper核心功能、压榨性能、自己动手丰衣足食

我偶然听说sqlsugar的性能比dapper强。对此我表示怀疑&#xff08;由于我一直使用的dapper存在偏见吧&#xff09;&#xff0c;于是自己测试了sqlsugar、freesql、dapper发现他们的给我的结果是sqlsugar>dapper>freesql&#xff08;这里并不是黑那个orm,毕竟不同orm功能不…

免费下载 80多种的微软推出入门级 .NET视频

.NET Core 3.0视频系列中宣布了80多个新的免费视频&#xff0c;这些视频同时放在Microsoft的Channel 9 和youtube上面。 在线观看由于跨洋网络效果不太好&#xff0c;下载到机器上慢慢看是最好的了。 所以Scott Hanselman 特别制作了一个页面 http://dot.net/videos。我把这个页…

数据库连接池原理及常用连接池介绍

一、背景介绍 1.1 什么是连接池 数据库连接池负责分配、管理和释放数据库连接&#xff0c;它允许应用程序重复使用一个现有的数据库连接&#xff0c;而不是再重新建立一个。 1.2 为什么要使用连接池 数据库连接是一种关键的有限的昂贵的资源&#xff0c;这一点在多用户的网…

DevOps案例研究|中华有为-解构华为软开云DevOps实践

内容来源&#xff1a;DevOps案例深度研究-华为软开云DevOps研究战队&#xff08;本文只展示部分PPT研究成果&#xff0c;更多细节请关注案例分享会&#xff0c;及本公众号。&#xff09;本案例内容贡献者&#xff1a;王英伟&#xff08;Topic Leader&#xff09;、蔡志超、陈安…

Excel催化剂插件安装过程升级,离线版全网公开下载!

Exce催化剂推出近2年&#xff0c;之前一直在推崇在线版的安装方式&#xff0c;但因安装步骤较多&#xff0c;可能吓退了一部分初级用户&#xff0c;此轮重新梳理&#xff0c;让安装方式更简单&#xff0c;也公开了离线版的安装文件&#xff0c;若部分用户不想使用在线版&#x…

包邮送88本畅销技术书

来给大家送一波福利&#xff0c;这次联系了 11个好友一起给各位送书&#xff0c;每个号送 8 本&#xff0c;一共 88本&#xff0c;还包邮哦。感谢清华大学出版社对本次活动的赞助。01 书怎么送很简单&#xff0c;关注下面的公众号&#xff0c;并在后台回复「抽奖」&#xff0c;…

字典树(Trie树)的原理与实现

一、概述 1.1 基本概念 字典树&#xff0c;又称为单词查找树&#xff0c;Tire数&#xff0c;是一种树形结构&#xff0c;它是一种哈希树的变种。 1.2 基本性质 根节点不包含字符&#xff0c;除根节点外的每一个子节点都包含一个字符从根节点到某一节点。路径上经过的字符连接…

C#刷遍Leetcode面试题系列连载(1) - 入门与工具简介

点击蓝字“dotNET匠人”关注我哟加个“星标★”&#xff0c;每日 7:15&#xff0c;好文必达&#xff01;什么要刷LeetCode大家都知道&#xff0c;很多对算法要求高一点的软件公司&#xff0c;比如美国的FLAGM (Facebook、LinkedIn、Amazon/Apple、Google、Microsoft)&#xff0…

.NET Core迁移前的准备工作

前段时间迁移.NET Core做了大量的试水和评估&#xff0c;今天整理一下分享给大家。大致有以下几个部分&#xff1a;1. .NET Core的由来2. 为什么要迁移.NET Core3. .NET Core3.X主要特性4. .NET Standard和.NET Core5. .NET Core Roadmap&版本选择接下来&#xff0c;我们详…

二叉排序树或二叉搜索树

一、二叉树基础 1.1 二叉排序树定义 二叉排序树&#xff08;Binary Sort Tree&#xff09;又称二叉查找(搜索)树&#xff08;Binary Search Tree&#xff09;。它是一颗空树&#xff0c;或者具有下列性质&#xff1a; 若它的左子树不为空&#xff0c;则左子树上所有结点的值…

沈向洋对于工程技术人员的十点经验分享

沈向洋目前是微软的执行副总裁&#xff0c;人工智能与研究部门的最高负责人。他是计算机视觉和图形学研究的世界级专家&#xff0c;是美国电气电子工程协会院士&#xff08;IEEE Fellow&#xff09;及是国际计算机协会院士&#xff08;ACM Fellow&#xff09;&#xff0c;曾任国…

.Net Core中间件和过滤器实现错误日志记录

1.中间件的概念ASP.NET Core的处理流程是一个管道&#xff0c;中间件是组装到应用程序管道中用来处理请求和响应的组件。每个中间件可以&#xff1a;选择是否将请求传递给管道中的下一个组件。可以在调用管道中的下一个组件之前和之后执行业务逻辑。中间件是一个请求委托&#…

最大堆、最小堆Java实现,解决TOP K问题

一、基础知识 1.1 什么是最大&#xff08;小&#xff09;堆 最大堆&#xff0c;最小堆类似&#xff0c;以下以最小堆为例进行讲解。 最小堆是满足以下条件的数据结构&#xff1a; 它是一棵完全二叉树所有父节点的值小于或等于两个子节点的值 1.2 什么是完全二叉树 除了最后…

使用Helm将ASP.NET Core应用程序部署到Kubernetes容器集群

在《容器化单页面应用中RESTful API的访问》以及《容器化单页面应用中Nginx反向代理与Kubernetes部署》两篇文章中&#xff0c;我介绍了一套容器化ASP.NET Core应用程序的方案&#xff0c;并对于Nginx反向代理的使用进行了介绍。在《使用Rancher在Microsoft Azure上搭建Kuberne…

微软如何利用机器学习改进Win 10更新体验

微软在 2018 年 4 月 Windows 更新时第一次大规模使用机器学习&#xff08;ML&#xff09;&#xff0c;ML 通过监测 PC 运行状况的六个核心领域&#xff08;例如总体可靠性&#xff09;&#xff0c;以确定功能更新过程是否顺利进行。而在 2019 年 5 月发布的更新推送中&#xf…