Vue でメニュー権限制御を実装するためのサンプルコード

Vue でメニュー権限制御を実装するためのサンプルコード

バックエンド管理システムで作業している場合、通常、メニュー権限制御に関連する問題に遭遇します。もちろん、この問題を解決する方法は、フロントエンド制御とバックエンド制御の 2 つしかありません。弊社の製品のイテレーション速度は比較的速いため、フロントエンドの制御ルーティングからバックエンドの制御ルーティングまでをイテレーションします。以下では、これら 2 つの方法の長所と短所、およびそれぞれの実装方法を紹介します (vue-router API に詳しくない学生は、まず公式 Web サイトにアクセスして API を確認してください)。

まず、プロジェクトの要件について簡単に説明します。下の図に示すように、第 1 レベルのメニューと第 2 レベルのメニューがあり、ログインする人によって異なるメニューが表示されます。

ルーティングのフロントエンド制御のアイデア:すべてのルーティングマッピングテーブルをメンテナンスのためにフロントエンドに持ってきます。つまり、すべてのメニューパスと対応するコンポーネントを router.js に書き込みます。後で、すべてを記述することの欠点について説明します。次に、左側のメニューをコンポーネント (sidebar.vue) に書き込み、このコンポーネントに次のようなデータを書き込み、ログイン時に取得したレベル値を通じてデータ内の固定メニューに hidden を追加しました。すると、フロントエンドは hidden に基づいてメニューを表示しました。

// router.js 疑似コード const Login = r => require.ensure([],()=>r(require('../page/login/Login.vue')),'login');
const Home = r => require.ensure([],()=>r(require('../page/Home.vue')),'home');
const Forbidden = r => require.ensure([],()=>r(require('../page/403.vue')),'forbidden');
const NotFound = r => require.ensure([],()=>r(require('../page/404.vue')),'notfound');
const Dashboard = r => require.ensure([],()=>r(require('../page/dashboard/Dashboard.vue')),'dashboard');
const SplashScreen = r => require.ensure([],()=>r(require('../page/splashScreen/SplashScreen.vue')),'splashScreen');
const AddSplashScreen = r => require.ensure([],()=>r(require('../page/splashScreen/AddSplashScreen.vue')),'addSplashScreen');

定数ルート = [
  {
    パス: '/'、
    リダイレクト: '/login'
  },{
    パス: '/login',
    コンポーネント: ログイン
  },{
    パス: '/404',
    コンポーネント: NotFound
  },{
    パス: '/home',
    コンポーネント: ホーム、
    リダイレクト: '/home/splashScreen',
    子供たち: [
      {
        パス: '/home/splashScreen',
        コンポーネント: SplashScreen、
        メタ: {
          タイトル:「国家奉仕 李白」
      },{
        パス: '/home/addSplashScreen',
        コンポーネント: AddSplashScreen、
        メタ: {
          タイトル: 国家奉仕 呂布
        }
      }
    ]
  }
];

以下はメニューコンポーネントの擬似コードです。

// サイドバー.vue
<テンプレート>
  <div class="サイドバー">
    <el-メニュー>
      ...
    </el-menu>
  </div>
</テンプレート>
<スクリプト>
  エクスポートデフォルト{
    データ() {
      戻る {
        ルート: [
          {
           インデックス: '1',
           タイトル:「国家奉仕ジャングル」
           アイコン: 'iconfont icon-guanggao',
           子供たち: [
            {
             インデックス: 'splashScreen',
             タイトル: 「李白」
             子供たち: []
            }, 
           ]
          },
          {  
            インデックス: '2', 
            タイトル:「国家奉仕ミッドフィールダー」、 
            アイコン:'iconfont icon-tuisongguanli-',
          }
        ]
      }
    },
    メソッド: {
      レベルを取得する(){
        定数レベル = sessionStorage.getItem('level');
        if(レベル === '0'){
          this.routes.forEach(関数(値){
            if(value.title == "国家奉仕シングル"){
              値.hidden = true;
              value.children.forEach(関数(値){
                if(value.title=="関羽"){
                  値.hidden = true;
                }
              })
            }
          })
        }そうでない場合(レベル === '1'){
          this.routes.forEach(関数(値){
            値.hidden = true
            value.children.forEach(関数(値){
              値.hidden = true;
            })
          })
        }
      }
    },
    作成された(){
      レベルを取得します。
    }
  }
</スクリプト>

これによりパーミッション機能は実現できますが、2つの問題があります。

  1. セッションに保存されるのはレベルです。ブラウザ コンソールを開いてレベルを手動で制御できるため、権限は無意味になります。
  2. パスを覚えている場合は、ブラウザの URL バーにパスを手動で入力し、Enter キーを押して任意のページを表示できます。これは、フロントエンドの router.js にすべてのルートをハードコーディングすることの欠点でもあります。

ここで、フロントエンドは、バックエンドから返されたレベルを通じてのみルーターを表示/非表示にします。これにより、フロントエンドがルート全体を維持することがより複雑になり、大きなリスクが生じます。

それでは、バックエンド制御ルーティングについて説明します。操作プロセスから始めましょう。ダッシュボードの中間ページを追加しました。このページには、さまざまなレベルの第 1 レベルのルートのみが表示されます。対応する第 1 レベルのルートをクリックすると、対応するページ ページに移動できます。このページにも、対応する第 2 レベルのルートのみが表示されます。

ここでは、「ルートの動的な追加」と「ナビゲーション ガード」と呼ばれる 2 つの新しい概念があります。つまり、フロントエンドの router.js では、ログイン ページや 404 ページなど、誰でもアクセスできるルーティング テーブルのみを記述します。その他のすべてのコンポーネント リソースは新しい components.js ファイルに書き込まれ、バックエンドから返された menuData を使用して components.js 内のキーがマッピングされます。対応するキーがある場合は、addRoutes を通じてルーターに動的に追加されます。ルートを動的に追加する方法は、ナビゲーション ガード beforeEach のフック関数に記述する必要があります。

ナビゲーション ガードとは、次のページにルーティングする前に実行する必要がある操作を意味します。つまり、ログイン後、ダッシュボード ページにジャンプします。このページに入る前に、バックエンドから要求された menuData を再カプセル化し、返されたデータをフロントエンドの components.js の権限に従ってマップする必要があります。最終データを addRoutes を通じてルートにプッシュすると、ダッシュボード ページに入ることができ、ダッシュボード ページを通じて対応するページ ページに入ることができます。つまり、ダッシュボード ページに入る前にすべての権限制御が完了しています。

ここでも小さな最適化ポイントがあります。上記のブラウザ メニュー バーから許可されていないページまたは存在しないページにアクセスする場合、空のページではなく 404 ページに直接アクセスできるように、vue-router の一致する優先順位に従って、ルート 404 と * このページを追加する必要があります。


// components.js すべてのページ リソース const home = () => import('../page/Home.vue');
const splashScreen = () => import('../page/splashScreen/SplashScreen.vue');
const addSplashScreen = () => import('../page/splashScreen/AddSplashScreen.vue');
const editSplashScreen = () => import('../page/splashScreen/EditSplashScreen.vue');

エクスポートデフォルト{
  家、
  スプラッシュスクリーン、
  スプラッシュスクリーンを追加、
  スプラッシュスクリーンの編集、
  
};

// router.js よく使われるページを書くだけですっきりしませんか? import Vue from 'vue';
'vue-router' から Router をインポートします。

Vue.use(ルーター);

const Login = () => import('../page/login/Login.vue');
const Home = () => import('../page/Home.vue');
const Forbidden = () => import('../page/403.vue');
const Dashboard = () => import('../page/dashboard/Dashboard.vue');
定数ルート = [
  {
    パス: '/'、
    リダイレクト: '/login'
  },{
    パス: '/login',
    コンポーネント: ログイン
  },{
    パス: '/403',
    コンポーネント: 禁止
  },
  {
    パス: '/dashboard',
    コンポーネント: ダッシュボード、
  },
];
デフォルトの新しいルーターをエクスポートします({
  モード: '履歴'、
  ルート: ルート、
  ベース: __dirname、
  リンクアクティブクラス: 'link-active'
  
})

// main.js 疑似コードは特定の関連ロジックのみを保持します import routeMap from './router/component.js';
const NotFound = () => import('./page/404.vue');
const formatRoutes = function (routes, routeData) {
  ルートデータの場合
    ルートデータ = {
      名前: 'ホーム',
      パス: '/home',
      // コンポーネントが正常に一致した場合にのみ、特定のページ コンポーネントにアクセスできます: routeMap['home'],
      子供たち: []、
    };
  }
  ルートの長さ && ルートごとに(ルート => {
    if(ルート.コンポーネント) {
      ルートコンポーネント = routeMap[route.component];
      ルートデータ.children.push({
        パス: route.path、
        名前: ルート.インデックス、
        コンポーネント: route.component、
        メタ: {
          タイトル: ルート.title,
        },
      })
    }
    ルートの子とルートの子の長さが等しい場合
      ルートをフォーマットします(route.children、routeData);
    }
  });
  routeData を返します。
};

isFetchRemote を true にします。

//フック関数を使用してルートをリダイレクトします router.beforeEach((to, from, next) => {
  const ユーザー名 = sessionStorage.getItem('ユーザー名');
  if(!username && to.path !== '/login'){
    次へ({パス: '/login'});
  }
  そうでない場合 (isFetchRemote && to.path !== '/login') {
    ajaxPost('/resourceAPI/getMenuData').then(res =>{
      res.status === 200 && res.data.errno === 0 の場合 {
        isFetchRemote = false;
        const menuData = res.data.result;
        localStorage.setItem('menudata', JSON.stringify(menuData));
        const routeData = formatRoutes(menuData);
        resourceApp.$router.addRoutes([routeData].concat([
          {名前:'404'、パス:'/404'、コンポーネント:見つかりません},
          {パス:'*'、リダイレクト:'/404'}]));
        リソースApp.$router.push({
          パス: to.path,
          クエリ: to.query
        });
      }
      それ以外 {
        isFetchRemote = true;
      }
      次();
    })
    .catch(エラー => {
      コンソールログ(エラー);
    });  
  }
  それ以外 {
    次();
  }
});


const resourceApp = 新しいVue({
  ルーター、
  レンダリング: h => h(App)
}).$mount('#app');

//menuData リクエストデータ // 第 1 レベル メニューと第 2 レベル メニューの違いは、第 1 レベル メニューにはコンポーネントの値があることです。たとえば、次の SMS 管理には第 1 レベル メニューのみがあります {
  "エラー番号": 0, 
  "errmsg": "権限が正常に取得されました", 
  "結果": [
    {
      "インデックス": "1", 
      "title": "ジャングルポジション", 
      "アイコン": "アイコンフォントアイコン-guanggao", 
      "子供たち": [
        {
          "インデックス": "スプラッシュスクリーン", 
          "アイコン": "", 
          "タイトル": "ナコルル", 
          「パス」: 「/home/splashAdverse」、 
          "コンポーネント": "splashAdverse", 
          "isShow": 真
        }, 
        {
          "インデックス": "スプラッシュスクリーンを追加", 
          "アイコン": "", 
          "タイトル": "李白", 
          「パス」: 「/home/addAdverse」、 
          "コンポーネント": "addAdverse", 
          "isShow": 偽
        }, 
        
      ]
    }, 
    {
      "インデックス": "メッセージ", 
      "title": "国家奉仕最高位", 
      "アイコン": "アイコンフォントアイコン-duanxinguanli", 
      "パス": "/home/message", 
      "コンポーネント": "メッセージ", 
      "子供たち": [
        {
          "インデックス": "メッセージを追加", 
          "title": "中国最高の関羽", 
          "アイコン": "", 
          "パス": "/home/addMessage", 
          "コンポーネント": "メッセージを追加", 
          "isShow": 偽
        }
        
      ]
    } 
  ]
}

サイドバーとダッシュボードの 2 つのコンポーネントは、セッションを通じてバックエンドのメニューデータを取得するだけで済みます。

// ダッシュボード疑似コード <テンプレート>
  <div class="nav_list">
    <div class="nav_list_item" v-for="navList 内のアイテム" @click="goPage(item)">
      <i :class="item.icon"></i>
      <h2>{{item.title}}</h2>
    </div>
  </div>            
</テンプレート>

<スクリプト> 
  作成された(){
    定数 routeArr = JSON.parse(localStorage.getItem('menudata'));
    this.navList = ルートArr;
  }, 
  メソッド: {
    ページへ移動(アイテム){
      // 1レベルメニューのみ if (item.component) {
        this.$router.push(item.path);
      }それ以外{
        // セカンダリメニューのデータ構造には子のパスのみが含まれます
        this.$router.push(item.children[0]['path']);
      }
    }
  }
</スクリプト>
// サイドバー疑似コード <script>
  エクスポートデフォルト{
    データ() {
      戻る {
        ルート: [],
      }
    },
    メソッド: {
      バウンサー(arr){
        arr.filter(function(val){ を返す
         戻り値 !(!val || val === "");
        });
      }
    },
    作成された(){
      const menuData = JSON.parse(localStorage.getItem('menudata'));
      // 現在のルーターのパスに対応するルーティング配列全体をマップします。letroutes = menuData.map((item)=>{

        // ルーティングは1レベルのみ if (item.component && item.path == this.$route.path) {
          コンソール.log(アイテム)
          返品商品;
        }それ以外{
          if(item.children[0]['path'] == this.$route.path){
            コンソール.log(アイテム)
            返品商品;
          }
        }
      })
      // 配列内の未定義、null、その他の空の値と false の値を削除します。 this.routes = this.bouncer(routes);
    }
  }
</スクリプト>

このように権限を制御することで、ブラウザ コンソールでセッションのレベルを変更したり、ブラウザのナビゲーション バーでパスを変更したりすると、ナビゲーション ガードに戻る、つまり、menuData を再取得する要求を送信します。ルートを追加した後、この値が一致しない場合は、404 に戻ります。もちろん、レベルを変更しても、権限の変更の制御は実現されません。これは、以前のフロントエンド制御ルートではなく、ルートを動的に取得するためです。

これで、Vue でメニュー権限制御を実装するためのサンプルコードに関するこの記事は終了です。Vue メニュー権限に関するより関連性の高いコンテンツについては、123WORDPRESS.COM の過去の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • Vueはバックグラウンド管理権限システムとトップバーの3段階メニュー表示機能を実装しています
  • vue addRoutes を使用して動的権限ルーティング メニューを実装する例

<<:  DockerプライベートライブラリHarborのアーキテクチャとコンポーネントの説明

>>:  MySQL は正常に起動するがポートをリッスンしない場合の解決策

推薦する

nodejsとyarnをインストールし、Taobaoソースプロセスレコードを構成する

目次1. nodejsをダウンロードする2. ダブルクリックしてインストール3. グローバル npm...

データ URI スキームを使用して Web ページに画像を埋め込む方法の紹介

データ URI スキームを使用すると、HTML、CSS、Javascript などで使用できるインラ...

FileZilla を使用して FTP ファイル サービスを素早く構築する方法

ファイルの保存とアクセスを容易にするために、FTPサービスが特別に構築されています。 FTP サーバ...

ubuntu16.04 で nginx を完全にアンインストールするための関連コマンド

nginx の概要nginx は、無料のオープンソースの高性能 HTTP サーバーおよびリバース プ...

Linux のパスワードを紛失した場合にリセットする方法

1. スタートアップメニューでは、カーソルを最初の行に移動します - eを押します 2. UTF-8...

Docker Toolboxを完全にアンインストールする方法

Docker Toolbox は、Windows 10 Professional より前のバージョン...

Vueは透かし効果を簡単に実現します

序文: Vueプロジェクトで透かし効果を使用するには、コンテナを指定できます効果画像: 1. コンテ...

MySQL の接続数が多すぎるエラーの原因と解決策

目次概要本日正午、開発およびテスト環境の MySQL サービスで接続数が多すぎるというエラーが報告さ...

Vueはコンピュータカメラを呼び出して写真機能を実現します

この記事の例では、コンピュータカメラを呼び出して写真機能を実現するためのvueの具体的なコードを参考...

Linux ネットワークプログラミング機能の簡単な分析

目次1.ソケットを作成する2. ソケットをバインドする3. 聞き手を作る。聞く4. 接続が受け入れら...

Nginx ベースの Mencached キャッシュ構成の詳細な説明

導入Memcached は分散キャッシュ システムです。Memcached には認証とセキュリティ制...

MySQL 8.0.11 MacOS 10.13 のインストールと設定方法のグラフィックチュートリアル

MacにMySQLデータベースをインストールし、環境変数を設定する手順を参考までに記録します。具体的...

初心者のための HTML コーディングガイドライン 30 選

1. HTMLタグは常に閉じる前のページのソース コードでは、次のような記述がよく見られます。 &l...

ブラウザのスクロールバーのスタイルを変更するための純粋な CSS の例

CSSを使用してブラウザのスクロールバーのスタイルを変更する ::-webkit-スクロールバー{ ...

Linux CentOS 6.5 ifconfig が IP を照会できない問題の解決方法

最近、何人かの友人から、仮想マシンに CentOS をインストールした後、ifconfig コマンド...