动态配置路由和侧边栏

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

使用技术:

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

相关推荐
GIS之路8 分钟前
GDAL 实现矢量裁剪
前端·python·信息可视化
是一个Bug11 分钟前
后端开发者视角的前端开发面试题清单(50道)
前端
Amumu1213813 分钟前
React面向组件编程
开发语言·前端·javascript
持续升级打怪中35 分钟前
Vue3 中虚拟滚动与分页加载的实现原理与实践
前端·性能优化
GIS之路38 分钟前
GDAL 实现矢量合并
前端
hxjhnct41 分钟前
React useContext的缺陷
前端·react.js·前端框架
前端 贾公子1 小时前
从入门到实践:前端 Monorepo 工程化实战(4)
前端
菩提小狗1 小时前
Sqlmap双击运行脚本,双击直接打开。
前端·笔记·安全·web安全
前端工作日常1 小时前
我学习到的AG-UI的概念
前端
韩师傅1 小时前
前端开发消亡史:AI也无法掩盖没有设计创造力的真相
前端·人工智能·后端