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>