最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

基于Vue實現(xiàn)簡單的權(quán)限控制

 更新時間:2023年07月26日 08:39:06   作者:前_端_小_小_白  
這篇文章主要為大家學(xué)習(xí)介紹了如何基于Vue實現(xiàn)簡單的權(quán)限控制,文中的示例代碼講解詳細(xì),具有一定的參考價值,需要的小伙伴可以了解一下

Vue+菜單權(quán)限+動態(tài)路由

實現(xiàn)原理:用戶登錄,服務(wù)端返回相關(guān)權(quán)限,進行持久化存儲,篩選動態(tài)路由,同時菜單欄也需動態(tài)渲染

靜態(tài)路由

靜態(tài)路由,也叫常量路由,即所有角色都可以訪問到的路由界面。如: login、 404

const constantRoute = [
  {
    //登錄
    path: '/login',
    component: () => import('@/views/login/index.vue'),
    name: 'login',
    meta: {
      title: '登錄', 
      hidden: true, 
      icon: 'Promotion', 
    },
  },
  {
    //登錄成功以后的布局路由
    path: '/',
    component: () => import('@/layout/layout.vue'),
    name: 'layout',
    meta: {
      title: '',
      hidden: false,
      icon: '',
    },
    redirect: '/home',
    children: [
      {
        path: '/home',
        name: 'home',
        component: () => import('@/views/home/index.vue'),
        meta: {
          title: '首頁',
          hidden: false,
          icon: 'House',
        },
      },
    ],
  },
  {
    //404
    path: '/404',
    component: () => import('@/views/404/index.vue'),
    name: '404',
    meta: {
      title: '404',
      hidden: true,
      icon: 'DocumentDelete',
    },
  },
]

對應(yīng)的菜單權(quán)限如圖:

動態(tài)路由

即不同角色所擁有的權(quán)限路由,一般登錄成功后,向后端發(fā)送請求,由服務(wù)器返回對應(yīng)的權(quán)限,然后進行篩選過濾。

//返回的用戶信息
[
  {
    "userId": 1,
    "avatar": "https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif",
    "username": "admin",
    "password": "111111",
    "desc": "平臺管理員",
    "roles": ["平臺管理員"],
    "buttons": ["cuser.detail"],
    "routes": [
      "Home",
      "User",
      "Role",
      "Permission",
      "Trademark",
      "Product",
      "Acl"
    ],
    "token": "Admin Token"
  },
]
//所有的權(quán)限路由
 const asyncRoute = [
        {
          path: '/acl',
          component: () => import('@/layout/index.vue'),
          name: 'Acl',
          meta: {
            title: '權(quán)限管理',
            icon: 'Lock',
          },
          redirect: '/acl/user',
          children: [
            {
              path: '/acl/user',
              component: () => import('@/views/acl/user/index.vue'),
              name: 'User',
              meta: {
                title: '用戶管理',
                icon: 'User',
              },
            },
            {
              path: '/acl/role',
              component: () => import('@/views/acl/role/index.vue'),
              name: 'Role',
              meta: {
                title: '角色管理',
                icon: 'UserFilled',
              },
            },
            {
              path: '/acl/permission',
              component: () => import('@/views/acl/permission/index.vue'),
              name: 'Permission',
              meta: {
                title: '菜單管理',
                icon: 'Monitor',
              },
            },
          ],
        },
        {
          path: '/product',
          component: () => import('@/layout/index.vue'),
          name: 'Product',
          meta: {
            title: '商品管理',
            icon: 'Goods',
          },
          redirect: '/product/trademark',
          children: [
            {
              path: '/product/trademark',
              component: () => import('@/views/product/trademark/index.vue'),
              name: 'Trademark',
              meta: {
                title: '品牌管理',
                icon: 'ShoppingCartFull',
              },
            },
            {
              path: '/product/attr',
              component: () => import('@/views/product/attr/index.vue'),
              name: 'Attr',
              meta: {
                title: '屬性管理',
                icon: 'ChromeFilled',
              },
            },
            {
              path: '/product/spu',
              component: () => import('@/views/product/spu/index.vue'),
              name: 'Spu',
              meta: {
                title: 'SPU管理',
                icon: 'Calendar',
              },
            },
            {
              path: '/product/sku',
              component: () => import('@/views/product/sku/index.vue'),
              name: 'Sku',
              meta: {
                title: 'SKU管理',
                icon: 'Orange',
              },
            },
          ],
        },
      ]

菜單權(quán)限

本次demo演示使用的是element-plus的el-menu組件。

在較為簡單的開發(fā)中,菜單我們經(jīng)常寫死,這也就導(dǎo)致了不同的角色所看到的菜單列表是一致的。

所以,一般實現(xiàn)動態(tài)路由,也要二次封裝一個對應(yīng)的菜單權(quán)限組件

實現(xiàn)步驟

通過pinia或者vuex全局狀態(tài)管理工具,定義一個全局狀態(tài) menuRoutes ,初始值為對應(yīng)的靜態(tài)路由數(shù)組

二次封裝menu組件,通過 menuRoutes,遞歸渲染展示不同的菜單欄

重點:需要使用到vue3的遞歸組件,因此需要定義組件名。同時 menuRoutes 需要以父傳子的方式傳遞

<template>
  <div>
    <template v-for="(item, index) in props.menuList" :key="item.path">
      <!-- 沒有子路由 -->  
      <template v-if="!item.children">
        <el-menu-item
          :index="item.path"
          v-if="!item.meta.hidden"
          @click="goRoute"
        >
          <template #title>
            <el-icon>
              <component :is="item.meta.icon" />
            </el-icon>
            <span>{{ item.meta.title }}</span>
          </template>
        </el-menu-item>
      </template>
      <!-- 只有一個子路由 (例如home頁,它是layout的子路由,但是只有一個,直接渲染home) -->
      <el-menu-item
        v-if="item.children && item.children.length == 1"
        :index="item.children[0].path"
        @click="goRoute"
      >
        <template #title>
          <el-icon>
            <component :is="item.children[0].meta.icon" />
          </el-icon>
          <span>{{ item.children[0].meta.title }}</span>
        </template>
      </el-menu-item>
      <!-- 有多個子路由 -->
      <el-sub-menu
        :index="item.path"
        v-if="item.children && item.children.length > 1"
      >
        <template #title>
          <el-icon>
            <component :is="item.meta.icon"></component>
          </el-icon>
          <span>{{ item.meta.title }}</span>
        </template>
         <!-- 子路由遞歸動態(tài)渲染 -->
        <Menu :menuList="item.children"></Menu>
      </el-sub-menu>
    </template>
  </div>
</template>
<script setup lang="ts">
import { ref, reactive, computed, onMounted, watch } from 'vue'
import { useRouter } from 'vue-router'
const $router = useRouter()
//獲取父組件傳遞的路由數(shù)組
interface Iprops {
  menuList: any[]
}
const props = withDefaults(defineProps<Iprops>(), {
  menuList: () => [],
})
const goRoute = (vc: any) => {
  $router.push(vc.index)
}
</script>
<script lang="ts">
export default {
  name: 'Menu',
}
</script>

登錄成功后,獲取用戶信息,從而獲取對應(yīng)的權(quán)限列表數(shù)據(jù),傳入所有之前定義好的權(quán)限路由,進行過濾。最后通過addRoute方法追加動態(tài)路由。

import { constantRoute, asyncRoute, anyRoute } from '@/router/routes'
//getUserInfo
 const res = await getUserInfo()
 let routes = this.filterAsyncRoute(
      _.cloneDeep(asyncRoute),
      res.data.checkUser.routes,
      )
 //修改菜單欄顯示
 this.menuRoutes = [...constantRoute, ...routes, anyRoute]
 //通過addRoute追加動態(tài)路由
   let activeRoutes = [...routes, anyRoute]
      activeRoutes.forEach((route) => {
        router.addRoute(route)
      })
 //過濾權(quán)限路由
 filterAsyncRoute(asyncRoute: RouteRecordRaw[], routes: RouteRecordName[]) {
      let result: RouteRecordRaw[] = []
      asyncRoute.forEach((item) => {
        if (routes.includes(item.name!)) {
          result.push(item)
          if (item.children) {
            item.children = this.filterAsyncRoute(item.children, routes)
          }
        }
      })
      return result
    },
  },

注意點:

1、每次過濾權(quán)限路由的時候,必須深拷貝一份asyncRoute,懂的都懂(引用類型數(shù)據(jù)是地址)

? 2、pinia中的數(shù)據(jù)是非持久性緩存的,所以一刷新數(shù)據(jù)就會丟失。解決方案:使用pinia的持久性插件或者路由鑒權(quán)的同時,在路由前置導(dǎo)航守衛(wèi),每次跳轉(zhuǎn)的時候,判斷pinia中是否存儲了用戶信息,如果沒有,重新調(diào)用getUserInfo方法,獲取用戶信息

? 3、是基于第二點,在組件外部通過同步語句獲取倉庫,是獲取不到的,必須通過如下方式獲取

import pinia from '@/store/index'
let userStore = useUserStore(pinia)

? 4、至此,我們成功實現(xiàn)了菜單權(quán)限+動態(tài)路由,但還有個bug

BUG:如果我們在動態(tài)路由頁面進行刷新,會導(dǎo)致白屏

原因:刷新頁面的時候,觸發(fā)了路由前置導(dǎo)航守衛(wèi),獲取用戶信息,如果獲取到了,就放行。但是放行的時候,動態(tài)路由還沒有加載完成! 得確保獲取完用戶信息且全部路由組件渲染完畢

解決辦法:next({...to})

意義:死循環(huán)加載,直至路由組件加載完畢

以上就是基于Vue實現(xiàn)簡單的權(quán)限控制的詳細(xì)內(nèi)容,更多關(guān)于Vue權(quán)限控制的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • vue如何從接口請求數(shù)據(jù)

    vue如何從接口請求數(shù)據(jù)

    本篇文章主要介紹了vue如何從接口請求數(shù)據(jù) ,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-06-06
  • vue 修改 data 數(shù)據(jù)問題并實時顯示的方法

    vue 修改 data 數(shù)據(jù)問題并實時顯示的方法

    今天小編就為大家分享一篇vue 修改 data 數(shù)據(jù)問題并實時顯示的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-08-08
  • Vue3.0 響應(yīng)式系統(tǒng)源碼逐行分析講解

    Vue3.0 響應(yīng)式系統(tǒng)源碼逐行分析講解

    這篇文章主要介紹了Vue3.0 響應(yīng)式系統(tǒng)源碼逐行分析講解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-10-10
  • 淺談Vue SSR 的 Cookies 問題

    淺談Vue SSR 的 Cookies 問題

    本篇文章主要介紹了淺談Vue SSR 的 Cookies 問題,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-11-11
  • Vue render函數(shù)使用詳細(xì)講解

    Vue render函數(shù)使用詳細(xì)講解

    vue中的render函數(shù),它返回的是一個虛擬節(jié)點vnode,也就是我們要渲染的節(jié)點,下面這篇文章主要給大家介紹了關(guān)于Vue中render函數(shù)的相關(guān)資料,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-01-01
  • vant組件庫之tag漸變色不起作用的原因及解決

    vant組件庫之tag漸變色不起作用的原因及解決

    這篇文章主要介紹了vant組件庫之tag漸變色不起作用的原因及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-04-04
  • React/vue開發(fā)報錯TypeError:this.getOptions?is?not?a?function的解決

    React/vue開發(fā)報錯TypeError:this.getOptions?is?not?a?function

    這篇文章主要給大家介紹了關(guān)于React/vue開發(fā)報錯TypeError:this.getOptions?is?not?a?function的解決方法,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-07-07
  • vue實現(xiàn)的上拉加載更多數(shù)據(jù)/分頁功能示例

    vue實現(xiàn)的上拉加載更多數(shù)據(jù)/分頁功能示例

    這篇文章主要介紹了vue實現(xiàn)的上拉加載更多數(shù)據(jù)/分頁功能,涉及基于vue的事件響應(yīng)、數(shù)據(jù)交互等相關(guān)操作技巧,需要的朋友可以參考下
    2019-05-05
  • vue cli3 配置proxy代理無效的解決

    vue cli3 配置proxy代理無效的解決

    今天小編就為大家分享一篇vue cli3 配置proxy代理無效的解決,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-10-10
  • vue實現(xiàn)簡單的購物車功能

    vue實現(xiàn)簡單的購物車功能

    這篇文章主要為大家詳細(xì)介紹了vue實現(xiàn)簡單的購物車功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-07-07

最新評論

徐闻县| 黔西| 阜阳市| 宽城| 临夏市| 林州市| 武山县| 景东| 平顶山市| 嘉善县| 怀安县| 尤溪县| 家居| 晋城| 章丘市| 贵阳市| 玛沁县| 盈江县| 顺平县| 淳化县| 房产| 吐鲁番市| 高淳县| 五寨县| 松原市| 天祝| 江达县| 辉南县| 三原县| 红安县| 揭东县| 依安县| 通化县| 六枝特区| 阜南县| 施秉县| 宣恩县| 滕州市| 长春市| 红桥区| 漳浦县|