vue3 + vite+element-Plus实现动态路由权限管理以及菜单渲染

router的index.js文件

import { createRouter, createWebHistory } from "vue-router"
import Login from '@/views/Login.vue'
import Layout from '@/views/Layout.vue'
import {getMenu} from '@/utils/api' //获取全部路由接口
import store from '@/store'
const routes = [
  { path: '/login', component: Login },
  { path: '/', name:'layout',component:Layout,redirect:'/home'}
]
 
const router = createRouter({
    history:createWebHistory(),
    routes
})

router.beforeEach((to,from,next)=>{
  if(!localStorage.getItem('token')){
    if(to.path == '/login'){
      next()
    }else{
      next('/login')
    }
  }else{
    next()
  }
})

if(!localStorage.getItem('token')){
  
}else{
  store.dispatch('routesList/getMenuList',{router,fn:()=>{router.replace(`${router.currentRoute.value.fullPath}`)}})
}

export default router

store中的routesList.js

import {getMenu} from '@/utils/api' //获取全部路由接口
const state={
    menuList:JSON.parse(localStorage.getItem('menus')) || []
}
const actions={
    async getMenuList(ctx,{router,fn}){
        let modules = import.meta.glob('../views/**/*.vue')
        const res = await getMenu({})
        if(res.data.data.tree.children.length > 0){
            const arr = res.data.data.tree.children
            ctx.commit('setMenuList',arr)
            localStorage.setItem('menus',JSON.stringify(arr))
            arr.forEach(item=>{
                router.addRoute('layout',{
                  path:`${item.path.split('/')[1]}`,
                  name:item.name,
                  component:modules[`../views/${item.component}.vue`]?modules[`../views/${item.component}.vue`]:null,
                })
                if(item.children && item.children.length > 0){
                  item.children.forEach(citem=>{
                    router.addRoute(`${item.name}`,{
                      path:citem.path,
                      name:citem.name,
                      component:modules[`../views/${citem.component}.vue`]?modules[`../views/${citem.component}.vue`]:null//从上面定义的组件数组中获取对应的组件
                    })
                  })
                }
            })
            fn?fn():''
        }
    }
}
const mutations={
    setMenuList(state,payload){
        state.menuList = payload
    }
}
const getters={}
export default{
    state,
    actions,
    mutations,
    getters,
    namespaced:true,
}

登录组件的代码逻辑如下:登录成功之后,直接调store中的action中的方法获取菜单数据,获取成功之后进行路由跳转

<template>
    <div class="wrapper">
        <div class="loginContainer">
            <div class="loginTitle">登录</div>
            <div class="formCon">
                <el-form ref="fromEl" :model="form" :rules="rules" label-width="120px">
                    <el-form-item prop="name" label="用户名" class="formItem">
                        <el-input v-model="form.name" />
                    </el-form-item>
                    <el-form-item prop="password" label="密码" class="formItem">
                        <el-input type="password" v-model="form.password" />
                    </el-form-item>
                    <el-form-item style="display: flex;" class="formItem">
                        <el-button style="flex:1" @click="submit(fromEl)" type="primary">登录</el-button>
                    </el-form-item>
                </el-form>
            </div>
        </div>
    </div>
</template>

<script setup>
    import {login} from '@/utils/api'
    import {useRouter} from 'vue-router'
    import {ref} from 'vue'
    import{useStore} from 'vuex'
    const store = useStore()
    const router = useRouter()
    const form = ref({
        name:'',
        password:''
    })
    const rules = ref({
        name: [
            {required: true, message: '请输入用户名', trigger: 'blur' },
        ],
        password: [
            {required: true, message: '请输入密码', trigger: 'blur' },
        ],
    })
    const fromEl = ref()
    const submit = fromEL => {
        if (!fromEL) return
        fromEL.validate((valid, fields) => {
            if (valid) {
                loginFn()
            }
        })
    }
    const loginFn = () => {
        login({
            username:form.value.name,
            password:form.value.password,
        }).then(res=>{
            localStorage.setItem('token',res.data.data.token)
            store.dispatch('routesList/getMenuList',{router,fn:()=>{router.replace('/home')}})
        })
    }
</script>

<style lang="scss">
.wrapper{
    height:100vh;
    width: 100vw;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: #f9f9f9;
    .loginContainer{
        width: 600px;
        height: 300px;
        background-color: #fff;
        box-shadow: 0 0 5px #999;
        border-radius: 16px;
        padding:10px;
        box-sizing: border-box;
        .loginTitle{
            height: 40px;
            width: 100%;
            text-align: center;
            line-height:40px;
            font-size: 24px;
            color:#333;
            font-weight: bold;
        }
        .formCon{
            height: calc(100% - 40px);
            width:100%;
            padding:20px;
            box-sizing: border-box;
            .formItem{
                height:40px;
                width: 100%;
                display: flex;
                align-items: center;
            }
        }
    }
}
</style>


layout布局组件主要负责菜单数据的渲染,从vuex中获取数据进行渲染

<template>
  <div style="display: flex;height: 100vh;width: 100vw;">
    <div style="width:200px;height:100vh;background-color: #545c64;">
      <el-scrollbar height="100vh" style="width:100%;">
        <el-menu
        :default-active="curRouter"
        class="el-menu-vertical-demo"
        background-color="#545c64"
        text-color="#fff"
        router
      >
        <template v-for="(item,index) in menus" :key="index">
          <template v-if="!item.children">
            <el-menu-item :index="item.path">
              <el-icon><Menu /></el-icon>
              <template #title>{
   
   { item?.title || '' }}</template>
            </el-menu-item>
          </template>
          <template v-else>
            <el-sub-menu :index="item.path" >
              <template #title>
                <el-icon><Menu /></el-icon>
                <span>{
   
   { item?.title || '' }}</span>
              </template>
              <template v-if="item.children && item.children.length > 0">
                <el-menu-item :index="citem.path" v-for="citem in item.children" :key="citem.id">
                  <template #title>
                      <el-icon><Menu /></el-icon>
                      <span>{
   
   { citem?.title || '' }}</span>
                  </template>
                </el-menu-item>
              </template>
            </el-sub-menu>
          </template>
        </template>
      </el-menu>
      </el-scrollbar>
        
    </div>
    <router-view></router-view>
  </div>
</template>

<script setup>
import {computed,} from 'vue'
import { useRoute } from 'vue-router';
import {useStore} from 'vuex'
const store = useStore()
const menus = computed(()=>store.state.routesList.menuList)
const curRouter = useRoute().fullPath
</script>

<style>
.el-menu{
  height: 100%;
}
</style>


 

猜你喜欢

转载自blog.csdn.net/Lsir1998/article/details/131498310