(二) Vue3 + Element-Plus 实现动态菜单栏

系列文章目录

  1. 系列介绍:Vue3 + Vite + TS 从零开始学习
  2. 项目搭建:(一) Vue3 + Vite + TS 项目搭建
  3. 实现动态菜单栏:(二) Vue3 + Element-Plus 实现动态菜单栏
  4. 实现动态面包屑:(三) Vue3 + Element-Plus 实现动态面包屑
  5. 实现动态标签页:(四) Vue3 + Element-Plus 实现动态标签页
  6. 实现动态主题色切换(demo):(五) Vue3 + Element-Plus 实现动态主题色切换
  7. 踩坑记录(持续更新):(六) Vue3 踩坑记录


一、引入依赖

开始前请确保已经安装以下依赖:

  1. VueX
$ npm i vuex --save
  1. Vue-Router
$ npm i vue-router --save
  1. NProgress
$ npm i nprogress --save

二、目录结构

|-src                   -- 主目录
---|api                 -- Ajax请求统一存放目录
------|auth-api.js      -- 路由数据获取接口
---|js                  -- JS脚本
------|permission.js    -- NProgress进度条数据处理
---|layout              -- 页面布局组件
------|sidebar.vue      -- 侧边栏布局组件
---|store               -- VueX
------|router.js        -- 路由全局常量

三、核心代码

1. auth-api.js

import request from '@/js/request'

export default {
  routers(data) {
    return request.post('/routers', data)
  }
}

2. permission.js

import router from '../router'
import store from '../store'
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
import { getCookie } from './cookie'
import Layout from '../layout/index.vue'
import ParentView from '../components/ParentView/index.vue'

NProgress.configure({ showSpinner: false })

const whiteList = ['/login', '/register']

router.beforeEach((to, from, next) => {
  NProgress.start()
  if (getCookie()) {
    if (to.path === '/login') {
      next({ path: '/' })
      NProgress.done()
    } else {
      if (store.state.user.menus.length === 0) {
        store.dispatch('GetInfo').then(res => {
          const menuIds = res.data.menuIds
          store.dispatch('GenerateRoutes', menuIds).then(routes => {
            filterRoutes(routes)
            routes.forEach(route => {
              router.addRoute(route)
            })
            next({ ...to, replace: true })
          })
        }).catch(() => {
          next()
        })
      } else {
        next()
      }
    }
  } else {
    if (whiteList.indexOf(to.path) !== -1) {
      next()
    } else {
      next(`/login?redirect=${to.fullPath}`)
      NProgress.done()
    }
  }
})

router.afterEach(() => {
  NProgress.done()
})


const filterRoutes = (routes) => {
  const accessRoutes = routes.filter(route => {
    let modules = import.meta.glob('../views/**/*.vue')
    if (route.component) {
      if (route.component === 'ParentView') {
        route.component = ParentView
      } else if (route.component === 'Layout') {
        route.component = Layout
      } else {
        route.component = modules[`../views/${route.component}.vue`]
      }
    }
    if (route.children && route.children.length) {
      filterRoutes(route.children)
    }
    return true
  })
  return accessRoutes
}

3.sidebar.vue

<template>
  <el-aside width="210px" class="aside-wrapper">
    <el-scrollbar>
      <el-menu :default-active="route.path" mode="vertical" :collapse-transition="false" router class="menu-wrapper">
        <el-sub-menu v-for="menu in menus" :key="menu.path" :index="menu.path">
          <template #title>
            <el-icon><component :is="menu.meta.icon" /></el-icon>
            <span>{{ menu.name }}</span>
          </template>
          <el-menu-item v-for="child in menu.children" :key="child.path" :index="child.path">
            <template #title>
              <el-icon><component :is="child.meta.icon" /></el-icon>
              <span>{{ child.name }}</span>
            </template>
          </el-menu-item>
        </el-sub-menu>
      </el-menu>
    </el-scrollbar>
  </el-aside>
</template>

<script lang="ts" setup>
import { computed } from 'vue'
import { useStore } from 'vuex'
import { useRoute } from 'vue-router'

const store = useStore()
const route = useRoute()
const menus = store.state.router.accessRoutes

</script>

4.router.js

import { authApi } from '@/api'
import constantRoutes from '@/router/routes'

const router = {
  state: {
    routes: [],
    accessRoutes: []
  },
  mutations: {
    SET_ROUTES: (state, routes) => {
      state.routes = routes
    },
    SET_ACCESSROUTES: (state, accessRoutes) => {
      state.accessRoutes = accessRoutes
    }
  },
  actions: {
    GenerateRoutes({ commit }, roleIds) {
      return new Promise(resolve => {
        authApi.routers(roleIds).then(res => {
          const accessRoutes = res.data
          filterRoutes('', accessRoutes)
          const routes = constantRoutes.concat(accessRoutes)
          commit('SET_ROUTES', routes)
          commit('SET_ACCESSROUTES', accessRoutes)
          resolve(routes)
        })
      })
    }
  }
}

const filterRoutes = (path, routes) => {
  routes.forEach(route => {
    const routePath = route.path
    if (route.parentId !== '0') {
      route.path = path + "/" + routePath
    }
    if (route.children && route.children.length) {
      filterRoutes(routePath, route.children)
    }
  })
}

export default router

四、最终效果

效果图

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue 3 和 Element Plus 的组合中,要实现侧边栏折叠的效果,可以使用`el-menu`作为根节点,并结合`el-menu-item`和`el-submenu`来构建菜单栏。 首先,在模板中使用`el-menu`作为整个菜单的容器,并设置`collapse`属性来控制侧边栏的折叠状态。当`collapse`为`true`时,侧边栏折叠,文字会隐藏。当`collapse`为`false`时,侧边栏展开,文字会显示。 ```html <template> <el-menu :collapse="isCollapse"> <!-- 菜单项 --> <el-menu-item index="1">菜单项1</el-menu-item> <el-menu-item index="2">菜单项2</el-menu-item> <!-- 子菜单 --> <el-submenu index="3"> <template #title> 子菜单 </template> <el-menu-item index="3-1">子菜单项1</el-menu-item> <el-menu-item index="3-2">子菜单项2</el-menu-item> </el-submenu> </el-menu> </template> ``` 然后,在data中定义一个`isCollapse`变量来控制折叠状态的切换。通过点击按钮或其他交互方式,修改`isCollapse`的值来实现侧边栏的折叠和展开。 ```javascript <script> export default { data() { return { isCollapse: true, // 初始化折叠状态为true,侧边栏默认折叠 }; }, }; </script> ``` 需要注意的是,`el-menu`标签本身希望里面嵌套的是`el-menu-item`、`el-submenu`、`el-menu-item-group`其中之一。因此,我们要根据菜单的结构来合理使用这些标签。 通过上述方法,你可以实现Vue 3 和 Element Plus 中的侧边栏折叠效果。根据`el-menu`的属性设置和交互操作来控制侧边栏的折叠和展开状态,从而实现文字的隐藏和显示效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值