动态配置路由和侧边栏

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

使用技术:

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

相关推荐
前端郭德纲1 小时前
浅谈React的虚拟DOM
前端·javascript·react.js
2401_879103682 小时前
24.11.10 css
前端·css
ComPDFKit3 小时前
使用 PDF API 合并 PDF 文件
前端·javascript·macos
yqcoder3 小时前
react 中 memo 模块作用
前端·javascript·react.js
优雅永不过时·4 小时前
Three.js 原生 实现 react-three-fiber drei 的 磨砂反射的效果
前端·javascript·react.js·webgl·threejs·three
神夜大侠6 小时前
VUE 实现公告无缝循环滚动
前端·javascript·vue.js
明辉光焱6 小时前
【Electron】Electron Forge如何支持Element plus?
前端·javascript·vue.js·electron·node.js
柯南二号7 小时前
HarmonyOS ArkTS 下拉列表组件
前端·javascript·数据库·harmonyos·arkts
wyy72937 小时前
v-html 富文本中图片使用element-ui image-viewer组件实现预览,并且阻止滚动条
前端·ui·html
前端郭德纲7 小时前
ES6的Iterator 和 for...of 循环
前端·ecmascript·es6