亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

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

實(shí)現(xiàn)原理:用戶登錄,服務(wù)端返回相關(guān)權(quán)限,進(jìn)行持久化存儲(chǔ),篩選動(dòng)態(tài)路由,同時(shí)菜單欄也需動(dòng)態(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)限如圖:

動(dòng)態(tài)路由

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

//返回的用戶信息
[
  {
    "userId": 1,
    "avatar": "https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif",
    "username": "admin",
    "password": "111111",
    "desc": "平臺(tái)管理員",
    "roles": ["平臺(tái)管理員"],
    "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)致了不同的角色所看到的菜單列表是一致的。

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

實(shí)現(xiàn)步驟

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

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

重點(diǎn):需要使用到vue3的遞歸組件,因此需要定義組件名。同時(shí) 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>
      <!-- 只有一個(gè)子路由 (例如home頁,它是layout的子路由,但是只有一個(gè),直接渲染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>
      <!-- 有多個(gè)子路由 -->
      <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>
         <!-- 子路由遞歸動(dòng)態(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)限路由,進(jìn)行過濾。最后通過addRoute方法追加動(dòng)態(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追加動(dòng)態(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
    },
  },

注意點(diǎn):

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

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

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

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

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

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

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

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

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

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

相關(guān)文章

最新評論