從壹開始 [Admin] 之五 ║ 實現『按鈕』級別權限配置

1、前情回顧

哈嘍你們好,在這個歡慶的日子裏,老張祝你們工做都能蒸蒸日上!今天正好也是社團成立的第一天,我也是但願今天能是個記念日,沾沾這個大喜慶!html

 

更新:前端

這篇文章獲得張善友,張隊的閱讀,並提供了另外一個方案,你們能夠看看,也是不錯的。vue

地址: https://mp.weixin.qq.com/s/CCGANQ5i0Ainq3jcSt7k4Agit

 

放假這兩天,卻是學到了不少東西,我這個也是認可的,昨天的事務提交,今天的按鈕級別的權限,都是羣裏小夥伴提供的方案和思路,我就是坐臥不安的寫到文章裏了,我總怕會說我是知識的偷盜者,固然我這個徹底是爲了社區,我畢竟一分錢沒有獲得,不管訪問量有多少,可能充其量就是數字好看。github

 

言歸正傳,還記得半年前(2019.02.27)的時候,個人 vue 項目之二:Blog.Admin 正式開源(https://github.com/anjoy8/Blog.Admin),當時打算作一個簡單的權限後臺系統,我本身想了經常使用的一些功能,固然有人說醜,有人說亂,可是也有人在本身項目和公司中使用,不過也是我付出心血的,並且也是完美的配合了 Blog.Core 項目,當時幾大設想功能中,遲遲有一個功能沒有實現,擱置了好久 —— 就是鈕級別的權限配置數據庫

 

 

 

 

當時我爲啥沒有作這個呢,有兩點考慮,一、是由於超級管理員我沒讓你們訪問,就怕誤操做數據,對別人觀看權限有影響;二、另外一個考慮,就是想把按鈕暴漏出來,看看是否是真的 test 測試帳號能不能刪除數據。後來我就開始思考,是時候把這個權限加進來了,就是沒有刪除的權限,刪除按鈕就不顯示,可是考慮了好久,被一個小知識點給卡住了,就是沒有想到如何動態事件綁定,這個不懂不要緊,我下文章會說到,前天由羣管理  @大黃瓜和@Kawhi 提供瞭解決思路和方案,眼前一亮,終於實現了這個功能。後端

投稿做者:@大黃瓜 and @Kawhiapi

效果預覽:我爲了防止大改,目前只在 「角色管理」 頁,增長了這個功能,後期所有替換;數組

在線地址:http://vueadmin.neters.club閉包

Github分支:主分支;

Tips : 目前我依然沒有開放 Admin 權限,因此若是想看所有效果,能夠下載本地,自行配置查看。

 

 

 

 下邊就開始正式講解,分紅了兩部分,步驟+重點知識說明,因此看步驟的時候,直接動手操做就好了,不用管爲何,下邊的第三部分——重點知識的說明,會簡單說說。

 

2、詳細設計步驟

一、後端微調,保存按鈕相關信息

不知道還有沒有小夥伴記得,我如今後臺的權限系統中,左側的導航條已經自動化了,所謂的自動化,就是已經徹底交給了數據庫,不管增長多少權限,不用前端或者後端進行操做,只須要配置便可達到目的,當時呢,我把左側的菜單和按鈕揉到了一張表裏,當時感受很不合理,可是如今又改起來簡單,得益於這個設計思路,因此此次咱們幾乎不用改什麼,只須要把按鈕信息給放出來便可,這裏有兩個小點:

一、Permission.cs 菜單表中,新建字段 Func ,用來存放當前按鈕所對應的方法事件;

二、/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);

三、在 RecursionHelper.cs 中,增長 IsButton 屬性,將數據庫數據,拼車 Tree 返回到前端;

這樣咱們就把按鈕數據配合着菜單數據一塊兒返回前端了,你能夠來查看下:

 

 

 

到這裏,咱們第一部分——後端數據就完成了,固然,若是你想更炫酷,能夠多增長字段,好比按鈕的樣式,或者其餘屬性等等等,這裏你確定明白,我就不細說了。

從下邊開始,咱們就開始說 Blog.Admin 項目了,請打開 VSCode ,來修改咱們的 Vue 項目:

 

二、修改後臺權限管理,添加按鈕事件

這個步驟很簡單,就是把上邊咱們創建的那個 Func 字段,給在頁面裏增刪改查一下就行了,具體的代碼自行修改便可。

 

 

 

 

三、控制「按鈕」不要和「菜單」展現衝突

剛剛咱們上邊說到了,把按鈕數據配合着菜單一塊兒開放了出來,那這個時候咱們要須要檢查一下,不能和菜單的展現起衝突,這裏我就直接說修改的地方了:

 

一、修改 Sidebar.vue 組件,讓按鈕的數據不要進行展現,具體的看看代碼就明白了,很簡單;

二、修改 src\router\index.js 中的動態路由注入方法,過濾掉按鈕數據;

 

 

到了這裏,咱們的第二部分——準備工做就作完了,接下來,就是本文的重中之重的重頭戲,設計這個工具欄了,那具體怎麼操做,這個時候我但願你能夠先暫停一下,先不要往下看,先本身腦中考慮一下,按照個人思路,就是按鈕數據也已經有了,如何設計這個公共組件呢?考慮五分鐘吧......

 

五分鐘後,假設你已經考慮過了,那我就開始正式說明。

 

四、設計工具欄組件——Toolbar.vue

 

既然要作成自動化的組件,就必定要抽象出來,那第一步就是創建一個組件,不能每一個頁面都寫類似的一堆代碼;

其實呢,咱們也要能夠配置,不能僅僅把按鈕給提出來,還應該有其餘的好比<input />搜索框等等,都應該放到工具欄裏;

必定要加載或者不加載,不能show or hide,這樣別人也會在查看元素的時候,看到;

 

綜上所述 ,個人設計是把表格裏的按鈕,所有提到了頂部,先給你們一個展現的效果圖,這個刪除顏色是我手動加的,你也能夠本身加個字段配置:

 

 

 

首先咱們建立組件,src\components\Toolbar.vue ,具體的代碼以下:

<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>

 

相信每一個人都能看的懂,只是字面意思能看得懂,其中的核心知識點就是 List for渲染,父給子傳值,子給父傳值,我下文會重點講到,其中 buttonList  數組的格式,很簡單,你能夠本身後端封裝一下,我這裏就偷懶了,直接使用的菜單的數據結果,就是上邊我 localstorage.routes 中的結構,畢竟我把按鈕和菜單共有一套嘛。

那如今咱們設計好了子組件——工具欄,接下來就要設計父組件了,傳遞數據和接受子組件廣播了。

 

 

五、將按鈕事件綁定到組件上

剛剛咱們說到了 ,在 Toolbar.vue 中,核心的內容,就是把動態的事件方法給推送到一個個父組件上,這裏是以 Role.vue 頁面舉例的,全部用到了  $emit("callFunction", item)  方法,這個若是你開發vue的話,確定都知道這個的,這個父子通信實例中,使用不少,具體的我在以前的文中中,也講到了,你能夠看看,這裏不細說,說白了一句話,就是子組件執行父組件方法。二十║Vue基礎終篇:傳值+組件+項目說明。其實到這個地方,我也想到了,可是問題來了:你能夠先看看 emit 的用法,使用 emit 通常都是傳遞數據,可是若是傳遞 function 的話,確定也是一個 name 的字符串,那父組件接受到這個 function name 的時候,很容易當成一個 data,若是強行執行,他們又不在一個對象裏,由於有閉包,如何讓頁面執行這個 function 呢?我思考了好久(說明本身學的不到家)。


這個就是這兩個月來困擾個人地方,前邊的思路和後邊的 Table 隔離我都想到了,只是這裏我沒有想到,看來仍是須要一些高級前端的朋友喲,前天聽到了一個 apply 方法後,我豁然開朗,原來能夠這樣,那下邊我就詳細的說一說,如何父組件執行事件:

在 src\views\User\Roles.vue 頁面呢,修改咱們的工具欄使用:

 

 

 

這種引用組件,在data中,定義 buttonList ,就不說,重點仍是要理解 @callFunction 這個必需要和子組件的 $emit 中的方法名一致。而後咱們定義 callFunction,用來動態執行一個個事件:

    callFunction(item) {//這個 item 就是咱們的 permission.cs 數據 this.filters = {
        name: item.search
      };//這裏是把子組件中的 search 內容,也接受過來 this[item.Func].apply(this, item);//核心就是要執行 apply 方法
    },

 

是否是很簡單,難點就在於,.apply()這個方法,下文會說到。這個 this ,就是固然父組件的內容,就是咱們執行能夠在子組件來調用父組件的方法了

 

 

 

這裏再說下

 

六、父組件獲取 ButtonList 數據

上邊咱們也說到了,咱們把 button 和 菜單揉在一塊兒了,因此咱們很簡單操做一下以前的數據就行,作一下篩選:

   // 在 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);
        }
      });
    }

 

OK,數據準備完畢。

 

七、修改 Table 組件,將工具欄與 Table 邏輯隔離

到了這裏就是最後一步了,咱們把以前的 tabel 右側 「操做欄」 刪掉,統一放到頂部,而後綁定數據,就能夠加載出來了,

 

 

 

如今咱們把操做欄給取消了,可是咱們如何獲取 scope.row  呢?是否是很麻煩,要修改不少呢,其實不是的。

 

八、Table 改成單選,經過點擊,選擇某行

這個功能特別簡單,思路就是經過單擊某一行,來獲取這個 table 的 row,這個 element 官網寫的很詳細,我就簡單的說一下吧:

    //觸發事件,獲取到這個row
    selectCurrentRow(val) {
      this.currentRow = val;
    },


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

 

而後只須要簡單的修改一下咱們的 edit 和 delete 方法便可,由於咱們已經拿到了這個 row:

 

 若是不選中某項,會彈出警告:

 

 

 

 搞定啦!是否是很簡單,幾乎沒有修改什麼,感受以前設計的方案還能夠吧,至少擴展仍是很不錯的!

到了這裏,咱們的動態按鈕權限功能,就已經徹底作完了,一個八個步驟,你們動手起來,搞一搞吧。

 

3、重點知識解析

 

一、組件 ——子傳父 & 父傳子

 這塊內容呢,其實咱們都已經講過不少遍了,父傳子很簡單,只須要定一個自定屬性便可,而後子組件接受,好比上文中的:

  <toolbar :buttonList="buttonList" @callFunction="callFunction"></toolbar>

 name: "Toolbar",
  data() {
    return {
      searchVal: "" //雙向綁定搜索內容
    };
  },
  props: ["buttonList"], //接受父組件傳值

 

比較複雜的就是 子傳父 了,重點仍是要了解一些 $emit 這個api,二十║Vue基礎終篇:傳值+組件+項目說明 我這篇文章寫的還算是詳細,若是仍是不懂,我們再一對一討論吧。

 

 

二、動態事件綁定—— 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)

 


舉個例子,以下所示:

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));   //30
console.log(mul(6,6));   //12

 

說句簡單的,我認爲就是在其餘地方,去調用某一個方法,很重要的一個點,就是 this 這個到底指向什麼,本身能夠好好調調。

 

 

三、動態路由過濾—— addRoutes

 這個在上邊的步驟裏我沒有說到,是由於咱們把 按鈕 給放出來之後,在動態菜單路由的時候,會出現重複的問題,因此咱們就須要坐下過濾,注意這個不是錯誤,是警告,意思就是咱們把一些重複的東西添加到路由裏了,路由會忽略掉,只不過給你們一個 warm 而已。

 

 

 

因此呢,我作了一個過濾,封裝了下 route.addRoutes——在 src\router\index.js 中,咱們過濾下重複路由,仍是遞歸:

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 true;
        }
        
    }
    
    var paramsFilt = params.filter(f);

    router.addRoutes(paramsFilt)
}

 

而後咋其餘的地方,將 router.addRoutes 統一都換成 router.$addRoutes 。可是這個目前還有一些小問題,我會後期繼續優化。

 

 

4、Github && Gitee

 

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

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

相關文章
相關標籤/搜索