系列文章目录

  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. auth-api.js
    • 2. permission.js
    • 3.sidebar.vue
    • 4.router.js
  • 四、最终效果

一、引入依赖

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

  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

四、最终效果

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

发表回复