解決 vue3 + vite + ts + pinia 權限問題方案

關于 vue3 + vite + ts + pinia 解決權限問題方案

1.目標

使用 pinia 保存服務器返回的權限列表生成路由列表數據和左側導航列表數據

2.問題描述

在使用?vue3 + pinia 實現(xiàn)此功能時,遇到了很多坑,如:

1.登錄后獲取權限添加到路由,但還未添加好路由時實際上已經出發(fā)了 next(),就跳轉到 404 頁面?

2.在 router.ts 中使用 pinia,pinia 報錯,后面查了才知道是路由初始化時 pinia 還未完成初始化??

3.路由請求成功并保存在本地后刷新頁面 頁面路由丟失然后就 404,是因為刷新后路由不會保存,需要重新拿到本地保存的權限列表添加到路由然后再next() ,但此處不能直接 next(),如果直接next(),還是會執(zhí)行到 404,因為當你刷新時進入到404,此時的 to.path 就是 404,就算你添加了路由,

next()還是會進入 404,所以就需要保存除404以外最后一次跳轉的路徑,即代碼中的?next({ path: menuStore.currentPath })

4.登錄后如果權限成功設置,如果此時我們退出登錄并且不刷新頁面,那么路由就會保存在本地,當我們換一個權限不同的用戶登錄時,路由里面則會添加個用戶的權限,就可以通過 url 訪問別人的權限,那么我們不可能強制刷新,這樣會影響用戶體驗,所以只能靜態(tài)刪除權限,退出時遍歷本地權限列表,刪除路由中的權限??router.removeRoute(‘此處是權限的name’)

以下是我的解決方案

router.ts

import "element-plus/theme-chalk/el-notification.css"

import { ElNotification } from "element-plus"

import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router"

import { useMenuStore } from '@/store/modules/menu'

import {

? ? getAllPremission,

? ? getRouterList

} from "@/utils/tools"

import errorRouter from './modules/error'

const LOGIN_NAME = '/login'

/* 導入路由文件模板 */

export let routerArray: RouteRecordRaw[] = errorRouter

// export let routerArray: RouteRecordRaw[] = getAllPremission()

const routes: Array<RouteRecordRaw> = [

? ? {

? ? ? ? path: '/login',

? ? ? ? name: 'login',

? ? ? ? component: () => import('@/view/login/index.vue')

? ? },

? ? { path: '/', redirect: { name: 'login' } },

? ? ...routerArray,

? ? {

? ? ? ? // 找不到路由重定向到404頁面

? ? ? ? name: "NotFont",

? ? ? ? path: "/:pathMatch(.*)",

? ? ? ? redirect: { name: "404" }

? ? }

]

const router = createRouter({

? ? history: createWebHashHistory(),

? ? routes,

? ? strict: false,

? ? // 切換頁面,滾動到最頂部

? ? scrollBehavior: () => ({ left: 0, top: 0 })

})

let initAddRoute = true // 是否是初始化添加路由

// 路由攔截

router.beforeEach(async (to, from, next) => {

? ? const token = localStorage.getItem('token')

? ? if (to.path === LOGIN_NAME) {

? ? ? ? // 此處添加初始化字段重置,因為如果退出后不刷新然后登錄不同的用戶,權限不一樣,會直接添加進路由,這樣新的用戶

? ? ? ? // 就可以通過 url 訪問前一個用戶的權限,所以退出時必須清空路由,此處重置字段用于重新獲取權限數據

? ? ? ? initAddRoute = true

? ? ? ? next()

? ? } else {

? ? ? ? if (!token) {

? ? ? ? ? ? ElNotification({

? ? ? ? ? ? ? ? title: '登錄提示',

? ? ? ? ? ? ? ? duration: 2000,

? ? ? ? ? ? ? ? message: '登錄過期,請重新登錄111!',

? ? ? ? ? ? ? ? type: 'warning',

? ? ? ? ? ? })

? ? ? ? ? ? next(LOGIN_NAME)

? ? ? ? } else {

? ? ? ? ? ? const menuStore = useMenuStore()

? ? ? ? ? ? // 如果當前跳轉的不是 404 頁面,則保存要跳轉的頁面名稱

? ? ? ? ? ? if (to.path !== "/404") {

? ? ? ? ? ? ? ? menuStore.setCurrentPath(to.path)

? ? ? ? ? ? }

? ? ? ? ? ? // 獲取保存的路由菜單并生成符合路由數據結構的列表的

? ? ? ? ? ? let routeList: Menu.MenuOptions[] = getRouterList(menuStore.routeList)

? ? ? ? ? ? // 登錄時獲取權限

? ? ? ? ? ? if (initAddRoute && from.name === "login") {

? ? ? ? ? ? ? ? let list = await menuStore.getPermiList()// 獲取權限

? ? ? ? ? ? ? ? routeList = await getRouterList(list)// 格式化權限

? ? ? ? ? ? }

? ? ? ? ? ? // 判斷當前是否是 初始化/刷新 =》添加路由,如果是并且本地保存了路由菜單,則添加進路由

? ? ? ? ? ? if (initAddRoute && routeList.length > 0) {

? ? ? ? ? ? ? ? routeList.forEach((item: any) => {

? ? ? ? ? ? ? ? ? ? router.addRoute(item)

? ? ? ? ? ? ? ? })

? ? ? ? ? ? ? ? initAddRoute = false

? ? ? ? ? ? ? ? // 初次進入時路由還未初始化好,如果直接執(zhí)行 next(),會跳轉到本地的路由 404,

? ? ? ? ? ? ? ? // 所以登錄或刷新時判斷當前是登錄并且跳轉的是 404,則重新觸發(fā)一次路由守衛(wèi),此時路由已初始化完,

? ? ? ? ? ? ? ? // 然后再執(zhí)行 next()

? ? ? ? ? ? ? ? if (from.name === "login" && to.name === "404") {

? ? ? ? ? ? ? ? ? ? next({ path: './configAdmin' })// 重新觸發(fā)導航守衛(wèi)并保存路徑

? ? ? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? ? ? next({ path: menuStore.currentPath })// 重新觸發(fā)導航守衛(wèi)一次

? ? ? ? ? ? ? ? }

? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? next()

? ? ? ? ? ? }

? ? ? ? ? ? // next()

? ? ? ? }

? ? }

})

export default router


menu.ts

import { defineStore } from "pinia"

import { MenuState } from '../interface'

import piniaPersistConfig from '@/config/piniaPersist'

import {

? ? getPermissionList,

} from '@/api/public'

import {

? ? getMenuList

} from "@/utils/tools"

// useMenuStore

export const useMenuStore = defineStore({

? ? id: "MenuState",

? ? state: (): MenuState => ({

? ? ? ? // menu collapse

? ? ? ? isCollapse: false,

? ? ? ? // routeList

? ? ? ? routeList: [],

? ? ? ? // menuList

? ? ? ? menuList: [],

? ? ? ? currentPath: ''

? ? }),

? ? getters: {},

? ? actions: {

? ? ? ? setCollapse() {

? ? ? ? ? ? this.isCollapse = !this.isCollapse

? ? ? ? },

? ? ? ? setRouteList(routeList: any) {

? ? ? ? ? ? this.routeList = routeList

? ? ? ? },

? ? ? ? setMenuList(menuList: any) {

? ? ? ? ? ? this.menuList = menuList

? ? ? ? },

? ? ? ? setCurrentPath(currentPath: string) {

? ? ? ? ? ? this.currentPath = currentPath

? ? ? ? },

? ? ? ? // 獲取權限列表

? ? ? ? async getPermiList() {

? ? ? ? ? ? const res: any = await getPermissionList()

? ? ? ? ? ? if (res.code === 200) {

? ? ? ? ? ? ? ? this.routeList = res.data

? ? ? ? ? ? ? ? this.menuList = getMenuList(res.data)

? ? ? ? ? ? }

? ? ? ? ? ? return res.data

? ? ? ? },

? ? },

? ? persist: piniaPersistConfig("MenuState")

})

utils/tools.ts

數據格式化方法,可根據自己業(yè)務的數據結構自行更改 :

// 獲取本地所有權限列表

export const getAllPremission = () => {

? ? const metaRouters = import.meta.glob("../router/modules/*.ts", { import: 'default', eager: true })

? ? let routerArray: any[] = []

? ? Object.values(metaRouters).forEach((item: any) => {

? ? ? ? item.map((val: any) => {

? ? ? ? ? ? routerArray.push(val)

? ? ? ? })

? ? })

? ? return routerArray

}

// 封裝 動態(tài)獲取到的權限 數據結構 => 路由

export const getRouterList = (list: Menu.RequestRouteItem[]) => {

? ? if (!list.length) return []

? ? let routeList: Menu.MenuOptions[] = getAllPremission().map(routeItem => {

? ? ? ? routeItem.children?.forEach((childrenItem: any) => {

? ? ? ? ? ? childrenItem.hasPermission = list.some((listItem: Menu.RequestRouteItem) => {

? ? ? ? ? ? ? ? if (listItem.children.length) {

? ? ? ? ? ? ? ? ? ? return listItem.children.some(child => {

? ? ? ? ? ? ? ? ? ? ? ? return childrenItem.meta!.title == child.permissionName

? ? ? ? ? ? ? ? ? ? })

? ? ? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? ? ? return childrenItem.meta!.title == listItem.permissionName

? ? ? ? ? ? ? ? }

? ? ? ? ? ? })

? ? ? ? ? ? if (childrenItem.meta!.title === '測試模版') {

? ? ? ? ? ? ? ? childrenItem.hasPermission = true

? ? ? ? ? ? }

? ? ? ? })

? ? ? ? return routeItem

? ? })

? ? // 生成最終權限路由列表

? ? routeList = routeList.map(item => {

? ? ? ? item.children = item.children?.filter(child => {

? ? ? ? ? ? return child.hasPermission

? ? ? ? })

? ? ? ? return item

? ? })

? ? return routeList

}


項目結構圖
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容