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 は正常に起動するがポートをリッスンしない場合の解決策

推薦する

JavaScript イベントバブリング、イベントキャプチャ、イベント委任の詳細な説明

1. イベントバブリング: JavaScript イベント伝播のプロセスでは、要素でイベントがトリガ...

Linux でユーザーを完全に削除する 2 つの方法

Linux 操作実験環境: Centos7 仮想マシンまず、共通ユーザーgubeiqingを作成しま...

MySQL の制限使用法とページングクエリステートメントのパフォーマンス分析の詳細な説明

使用制限クエリ ステートメントを使用する場合、多くの場合、データの最初の数行または中間行を返す必要が...

EclipseでTomcatを作成する原理の詳細な説明

ローカルEclipse上にTomcatサーバーを作成する場合、 tomcaインストールディレクトリの...

MySQL の指定文字によるマージと分割の例のチュートリアル

序文指定した文字による結合または分割は一般的なシナリオです。MySQL では結合の記述は比較的簡単で...

Javascript のスコープとクロージャの詳細

目次1. 範囲2. スコープチェーン3. 語彙の範囲5. 閉鎖の適用6. クロージャの欠陥7. 閉会...

CSS 評価効果の星の例

何?何のスターコートですか?さて、もっとわかりやすくするために写真を見てみましょう。 よく見ると、パ...

AWS無料サーバーアプリケーションとネットワークプロキシ設定チュートリアルの詳細な説明

目次予防必要条件AWSアカウントを申請する仮想マシンの申請と有効化仮想マシンを申請するセキュリティグ...

25 div+css プログラミングのヒントとコツ

1. ul タグには、Mozilla ではデフォルトでパディング値がありますが、IE ではマージン値...

Linux での Docker のインストールと展開の例

以下の記事を読んだ後、プロジェクトをサーバーにデプロイできます。Tomcat、JDK、MySQL な...

ローカル yum ソースの設定、国内 yum ソースの設定、epel ソースの設定を行う Linux の手順

1. ローカルyumソースを設定する1. ISOイメージをマウントする マウント -o loop /...

MySQL の遅いクエリの落とし穴

目次1. 遅いクエリ構成1-1. スロークエリを有効にする2. 遅いクエリSQLの分析を説明する3....

MySQL インデックスがソートに与える影響の分析例

この記事では、例を使用して、MySQL インデックスがソートに与える影響を説明します。ご参考までに、...

非常に詳細な基本的なJavaScript構文ルール

目次01 JavaScript(略称:js) js は 3 つの部分に分かれています。 JavaSc...

MySQLデータベースバックアップのさまざまな実装方法の概要

この記事では、MySQL データベースのバックアップを実装するさまざまな方法について説明します。ご参...