美文网首页
Vue3 & TypeScript组件嵌套:动态菜单儿(使用el

Vue3 & TypeScript组件嵌套:动态菜单儿(使用el

作者: 牛会骑自行车 | 来源:发表于2023-04-19 18:11 被阅读0次
    效果图: 展开
    点:
    1. 首先我们的项目中需要安装unplugin-vue-components/vite和unplugin-auto-import/vite,可以自动引用组件(我看另外一个开源项目中直接在页面中引用、使用,在组件中就不需要引用了。。。。回头可以试一下子);或者将所有的组件的引用封装在一起,可以查看 vue ---- 全局组件统一管理(这篇是用vue2写的,用于vue3得稍作改动);

    2. 组件思路是:el-menu,里面是一会儿将要嵌套的小组件~小组件循环每个路由单项;

    3. 组件单项思路(将menu-item抽出来):看el-menu中从有没有子项就分为el-sub-menu和el-menu-item两种:el-sub-menu是右侧有小箭头,代表该项存在子菜单,可展开;el-menu-item没有小箭头,代表没有子菜单;

    4. 在menu-item中进行判断,以有无子项为准,选择采用以上哪种子项;

    5. 渲染el-menu时需要设置defaultActive默认项,一般为首页唯一值dashboard;但如果因为页面刷新,需要实时监听当前路由,再将唯一值赋值给defaultActive;

    6. 图中菜单右侧属于el-menu的部分明显有一道透明的边边。因为el-menu有一个1px透明的边边。。。。我们在组件中设置以下代码即可;

    7. 动态路由的过滤方法全部放在store的permission模块中;在每个页面渲染之前需要先用守卫拦一下子,一般在src下的permission.ts中完成:

      • 需要给出一个完整的侧边路由和固定路由,方便获取权限后进行过滤(pinia);
      • 路由守卫beforeEach中需要做出的操作内容:
        1. 先判断是否为登录态,不是直接去LoginPage,是就接下来判断是否有角色分配;
        2. 有角色分配直接next放行,无角色分配就先获取角色;
        3. 根据角色(代码中的modulePermission)对完整路由进行过滤(分别对路由进行添加、给侧边菜单赋值);

    思路就是这,下面是代码

    sidebar代码:
    <script lang="ts" setup>
    import Router from "@/router";
    import store from '@/store/index'
    
    const {app, permission} = store();
    
    const list = computed(() => {
        return permission.setSideBarRoutes(permission.state.filterRoutes);
    })
    const defaultActive = computed(() => {
        return app.state.activeRoute;
    })
    // 页面方法
    const toHome = () => {
        Router.push({
            path: '/'
        });
    }
    
    </script>
    
    <template>
        <div class="sidebar-view">
            <div class="logo-view" @click="toHome">
                <img src="https://img1.baidu.com/it/u=3940612183,1877024013&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1682010000&t=426150bc8f1786722713152075ee49f3"
                    alt="" class="logo">
                <div v-if="!app.state.isCollapse">嘻嘻嘻嘻嘻嘻嘻</div>
            </div>
            <div class="menu-view">
                <Menu :list="list" :defaultActive="defaultActive" :collapse="app.state.isCollapse" />
            </div>
        </div>
    </template>
    
    <style lang="scss" scoped>
    .sidebar-view {
        height: 100%;
        width: 100%;
        background: $primary;
        color: #fff;
    }
    
    .logo-view {
        height: 50px;
        display: flex;
        justify-content: space-evenly;
        align-items: center;
    
        cursor: pointer;
    
        .logo {
            width: 40px;
            height: 40px;
            border-radius: 50%;
            object-fit: cover;
        }
    }
    
    .menu-view {
        width: 100%;
        height: calc(100% - 50px);
        overflow-y: auto;
        overflow-x: hidden;
    }
    </style>
    
    menu组件代码:
    <template>
        <el-menu class="el-menu-vertical-demo" :default-active="props.defaultActive" router :collapse="collapse"
            :background-color="commonStyle.primary" text-color="#fff" active-text-color="#304156">
            <menu-item v-for="item in list" :key="item.name" :menu="item" />
        </el-menu>
    </template>
    
    <script setup lang="ts">
    import type {RouteType} from '@/store/types'
    import commonStyle from '@/assets/css/variables.module.scss'
    
    
    interface PropsType {
        list: RouteType[]
        defaultActive?: string
        collapse?: boolean
    }
    const props = withDefaults(defineProps<PropsType>(), {
        defaultActive: '/dashboard'
    })
    
    </script>
    
    <style lang="scss" scoped>
    .el-menu {
        border: none!important;
    }
    </style>
    
    menuItem组件代码:
    <script setup lang="ts">
    const props = defineProps(['menu'])
    
    const menu = computed(() => {
        return props.menu;
    })
    </script>
    
    <template>
        <el-sub-menu v-if="menu.children" :index="menu.fullPath">
            <template #title>
                <el-icon>
                    <component v-if="menu.icon" :is="menu.icon"></component>
                </el-icon>
                <span>{{ menu.meta.title }}</span>
            </template>
            <menu-item v-for="item in menu.children" :key="item.name" :menu="item" />
        </el-sub-menu>
        <el-menu-item v-else :index="menu.fullPath">
            <el-icon>
                <component v-if="menu.icon" :is="menu.icon" class="menu-icon"></component>
            </el-icon>
            <template #title>{{ menu.meta.title }}</template>
        </el-menu-item>
    </template>
    
    <style lang="scss">
    .el-menu-item.is-active {
        font-weight: 700;
    }
    </style>
    
    src下的permission.ts代码:
    
    import Router,{addRouter} from '@/router'
    import store from '@/store/index'
    
    Router.beforeEach(async (to, from, next) => {
        const { permission, app } = store();
        const whiteList = ['/login', '/dashboard'];
        const toPath = to.path;
        const isLogin = localStorage.getItem("NJX_TOKEN");
    
        if (isLogin) {
            // 权限判断
            const hasPermission = permission.state.modulePermission.length > 0;
            if (hasPermission) {
                next();
            } else {
                permission.clearPermission();
                try {
                    // 获取角色
                    await permission.getPermission();
                    app.setActiveRoute(to);
                    addRouter(permission.state.filterRoutes);
    
                    next({
                        ...to,
                        replace: true
                    })
                } catch { 
                    Router.push('/login')
                }
            }
        }
        else {
            const isWhite = whiteList.findIndex(item => toPath === item) !== -1;
            if (!isWhite) {
                Router.push('/login')
            } else {
                next()
            }
        }
    })
    
    Router.afterEach((to) => {
        document.title = to.meta.title as string;
    })
    

    tada~~一个可以配合动态权限的菜单就完成啦~

    明天加一个login页的redirect哈

    相关文章

      网友评论

          本文标题:Vue3 & TypeScript组件嵌套:动态菜单儿(使用el

          本文链接:https://www.haomeiwen.com/subject/quxkjdtx.html