动态配置路由和侧边栏

需求背景: 后台管理需要给用户进行权限划分,用户登录后台管理后接口根据权限返回不同的侧边栏菜单和对应的页面及功能权限数据,然后前端根据数据进行动态配置侧边栏展示和对应的路由

使用技术:

vuex 实现store管理应用数据

配置store

javascript 复制代码
import { createStore } from 'vuex'
import menu from './modules/menu'
import user from './modules/user'
import permission from './modules/permission'
import getters from './getters'
import operateMap from './modules/operateMap'

const store = createStore({
    modules: {
        menu,
        user,
        permission,
        operateMap,
    },
    getters,
})

export default store

menu: 用来请求菜单列表数据,并保存数据

permission: 将根据权限返回的菜单列表数据处理为侧边栏可用的数据,和路由数据,针对的是页面

operateMap: 将根据权限返回的菜单列表数据处理为功能权限数组数据,以供具体页面的功能模块限制,针对的是具体功能

router配置

在说到具体的模块对应的store之前,先来看一下router中的配置

1. 静态路由表
dart 复制代码
export const constantRoutes = [
    {
        path: '/',
        name: '$404',
        component: () => import('@/views/default/index.vue'),
    },
    {
        path: '/:pathMatch(.*)*',
        redirect: '/',
    },
    {
        path: '/bigclass/login',
        name: '$login',
        component: () => import('@/views/login.vue'),
    },
    ]

静态路由是用来存放一些不因权限配置而更改的页面路由,比如登录,默认404页面,以及需求中的其他页面等

2. 生成路由
javascript 复制代码
const router = createRouter({
    history: createWebHistory(import.meta.env.BASE_URL),
    scrollBehavior(to, from, savedPosition) {
        if (savedPosition) {
            return savedPosition
        } else {
            return { x: 0, y: 0 }
        }
    },
    routes: constantRoutes,
})

先根据静态路由生成router

3. 动态请求菜单列表
lua 复制代码
// 判断该路由是否需要登录权限
    if (islogin) {
        // 判断当前的token是否存在 ; 登录存入的token
        if (store.getters.routes.length === 0) {
            store
                .dispatch('menu/getMenuList')
                .then(menuList => {
                    store.dispatch('operateMap/configOperateMap', menuList)
                    store.dispatch('permission/generateRoutes', { menuList }).then(addRoutes => {
                        addRoutes.forEach(item => {
                            // 针对特殊的路由特殊处理
                            if (hideMainPaddingWhiteList.indexOf(item.path) !== -1) {
                                item.meta = { ...item.meta, hideMainPadding: true }
                            }
                            router.addRoute(item)
                        })
                        store.dispatch('permission/generateSliderNavs', { menuList }).then(sliderNavs => {
                            if (sliderNavs.length > 0) {
                                next({ path: addRoutes[0].path, replace: true })
                            } else {
                                next({ path: '/', replace: true })
                            }
                        })
                    })
                })
                .catch(error => {
                    error && message.error(error)
                })
        } else {
            next()
        }
    } else {
        next({
            path: '/bigclass/login',
        })
    }

路由的配置到这里基本完成了,这里需要判断是否登录,已登录再判断是否需要请求菜单列表,然后是对菜单数据处理为相应的动态路由表,侧边栏数据以及功能权限数据,在配置完侧边栏之后,需要动态地定位到路由表的第一条数据

菜单数据的store

javascript 复制代码
const getDefaultState = () => {
    return {
        menuList: [],
    }
}
const state = getDefaultState()

const mutations = {
    SET_MENULIST: (state, menuList) => {
        state.menuList = menuList
    },
    RESET_STATE: state => {
        Object.assign(state, getDefaultState())
    },
}

const actions = {
    // 获取菜单列表
    getMenuList({ commit }) {
        return new Promise((resolve, reject) => {
            Inter.getMenuList()
                .then(response => {
                    if (requestResult.success(response)) {
                        if (response.data.list) {
                            commit('SET_MENULIST', response.data.list)
                            resolve(response.data.list)
                        }
                    } else {
                        reject()
                    }
                })
                .catch(error => {
                    reject(error)
                })
        })
    },
    resetState({ commit }) {
        return new Promise(resolve => {
            commit('RESET_STATE')
            resolve()
        })
    },
}
export default {
    namespaced: true,
    state,
    mutations,
    actions,
}

主要是请求菜单数据,并保存下来

permission

1. 侧边栏数据处理
javascript 复制代码
const configSliderNavs = menuList => {
    const sliderNavs = generateSliderNavs(menuList)
    return sliderNavs
}
// 获取侧边栏配置数据
const generateSliderNavs = menuList => {
    return (
        menuList &&
        menuList.map(({ name, icon, code, submenuList }) => {
            return {
                name,
                iconComp: icon,
                route: toReplaceName(code),
                child:
                    submenuList &&
                    submenuList.map(({ name, code }) => {
                        return {
                            name,
                            route: toReplaceName(code),
                        }
                    }),
            }
        })
    )
}
2. 路由数据处理
ini 复制代码
// 获取动态添加的router数据
const configRoutes = menuList => {
    const routers = []
    if (menuList) {
        const modules = import.meta.glob('@/views/**/index.vue')
        menuList.forEach(firtItem => {
            const parent = toReplaceName(firtItem.code)
            if (firtItem.submenuList) {
                firtItem.submenuList.forEach(secondItem => {
                    // 二级页面
                    const routeName = toReplaceName(secondItem.code)
                    const routePath = toReplacePath(secondItem.code)
                    let secondParam = ''
                    if (secondItem.params && secondItem.params.length > 0) {
                        secondItem.params.forEach(item => {
                            secondParam = secondParam + `:${item}`
                        })
                        secondParam = `/${secondParam}`
                    }
                    const title = secondItem.name
                    routers.push({
                        path: routePath + secondParam,
                        name: routeName,
                        meta: {
                            highLightRoute: routeName,
                            parent,
                            title,
                            showHeader: true,
                        },
                        component: modules[`/src/views${routePath}/index.vue`],
                    })
                    if (secondItem.submenuList) {
                        //三级页面
                        secondItem.submenuList.forEach(thirdItem => {
                            const thirdRouteName = toReplaceName(thirdItem.code)
                            const thirdRoutePath = toReplacePath(thirdItem.code)
                            let thirdParam = ''
                            if (thirdItem.params && thirdItem.params.length > 0) {
                                thirdItem.params.forEach(item => {
                                    thirdParam = thirdParam + `:${item}`
                                })
                                thirdParam = `/${thirdParam}`
                            }
                            const thirdTitle = thirdItem.name
                            routers.push({
                                path: thirdRoutePath + thirdParam,
                                name: thirdRouteName,
                                meta: {
                                    highLightRoute: routeName,
                                    parent,
                                    title: thirdTitle,
                                    showHeader: thirdItem.showHeader,
                                    showBreadcrumb: thirdItem.showBreadcrumb,
                                    breadRoutes: [{ path: routePath, breadcrumbName: title }],
                                },
                                component: modules[`/src/views${thirdRoutePath}/index.vue`],
                            })
                        })
                    }
                    if (secondItem.buttonList) {
                        // 另一个三级数据
                        secondItem.buttonList.forEach(buttonItem => {
                            if (buttonItem.routes) {
                                buttonItem.routes.forEach(buttonRoute => {
                                    const buttonRouteName = toReplaceName(buttonRoute.code)
                                    const buttonRoutePath = toReplacePath(buttonRoute.code)
                                    let buttonParam = ''
                                    if (buttonRoute.params && buttonRoute.params.length > 0) {
                                        buttonRoute.params.forEach(item => {
                                            buttonParam = buttonParam + `:${item}`
                                        })
                                        buttonParam = `/${buttonParam}`
                                    }
                                    const buttonTitle = buttonRoute.name
                                    routers.push({
                                        path: buttonRoutePath + buttonParam,
                                        name: buttonRouteName,
                                        meta: {
                                            highLightRoute: routeName,
                                            parent,
                                            title: buttonTitle,
                                            showHeader: buttonRoute.showHeader,
                                            showBreadcrumb: buttonRoute.showBreadcrumb,
                                            breadRoutes: [{ path: routePath, breadcrumbName: title }],
                                        },
                                        component: modules[`/src/views${buttonRoutePath}/index.vue`],
                                    })
                                })
                            }
                        })
                    }
                })
            }
        })
    }
    return routers
}
state, mutations,actions配置
javascript 复制代码
const getDefaultState = () => {
    return {
        routes: [],
        addRoutes: [],
        sliderNavs: [],
    }
}
const state = getDefaultState()

const mutations = {
    SET_ROUTES: (state, routes) => {
        state.addRoutes = routes
        state.routes = constantRoutes.concat(routes)
    },
    SET_SLIDERNAVS: (state, sliderNavs) => {
        state.sliderNavs = sliderNavs
    },
    RESET_STATE: state => {
        Object.assign(state, getDefaultState())
    },
}

const actions = {
    generateRoutes({ commit }, { menuList }) {
        return new Promise(resolve => {
            // 这里是拼凑出来新的路由
            const accessedRoutes = configRoutes(menuList)
            commit('SET_ROUTES', accessedRoutes)
            resolve(accessedRoutes)
        })
    },
    generateSliderNavs({ commit }, { menuList }) {
        return new Promise(resolve => {
            const sliderNavs = configSliderNavs(menuList)
            commit('SET_SLIDERNAVS', sliderNavs)
            resolve(sliderNavs)
        })
    },
    resetState({ commit }) {
        return new Promise(resolve => {
            commit('RESET_STATE')
            resolve()
        })
    },
}

功能权限对应的某一个模块的配置

dart 复制代码
import { defineStore } from 'pinia'
import { useStore } from 'vuex'

export const useAccountStore = defineStore('crm-account', () => {
    const store = useStore()
    // 是否显示新建/编辑
    const showCreate = computed(() => {
        return store.getters.operateMap.indexOf('$permission_account_operate') !== -1
    })
    // 是否显示批量导入
    const showBatchImport = computed(() => {
        return store.getters.operateMap.indexOf('$permission_account_operate_batch_import') !== -1
    })
    // 是否显示冻结/恢复/离职
    const showStatus = computed(() => {
        return store.getters.operateMap.indexOf('$permission_account_operate_status') !== -1
    })
    // 是否显示复制密码
    const showCopyPassword = computed(() => {
        return store.getters.operateMap.indexOf('$permission_account_operate_password_copy') !== -1
    })
    // 是否显示重置密码
    const showResetPassword = computed(() => {
        return store.getters.operateMap.indexOf('$permission_account_operate_password_reset') !== -1
    })
    return {
        showCreate,
        showBatchImport,
        showStatus,
        showCopyPassword,
        showResetPassword,
    }
})

对于功能权限这块,笔者采用的是pinia来管理,也可根据自己的项目结构用vuex

相关推荐
一个处女座的程序猿O(∩_∩)O1 小时前
小型 Vue 项目,该不该用 Pinia 、Vuex呢?
前端·javascript·vue.js
hackeroink4 小时前
【2024版】最新推荐好用的XSS漏洞扫描利用工具_xss扫描工具
前端·xss
迷雾漫步者6 小时前
Flutter组件————FloatingActionButton
前端·flutter·dart
向前看-6 小时前
验证码机制
前端·后端
燃先生._.7 小时前
Day-03 Vue(生命周期、生命周期钩子八个函数、工程化开发和脚手架、组件化开发、根组件、局部注册和全局注册的步骤)
前端·javascript·vue.js
高山我梦口香糖8 小时前
[react]searchParams转普通对象
开发语言·前端·javascript
m0_748235248 小时前
前端实现获取后端返回的文件流并下载
前端·状态模式
m0_748240259 小时前
前端如何检测用户登录状态是否过期
前端
black^sugar9 小时前
纯前端实现更新检测
开发语言·前端·javascript