话不多说,直接上代码,首先看看页面解构 (这里使用的是模拟import导入的数据):
路由文件:router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
import dataList from '../assets/data' //模拟数据
import { defineAsyncComponent } from 'vue'
import { useCounterStore } from '@/stores/counter' // 引入 pinia库
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'shouye',
redirect: 'home'
},
{
path: '/home',
name: 'home',
component: HomeView
},
{
path: '/about',
name: 'about',
component: () => import('../views/AboutView.vue')
}
]
})
// 前置路由守卫
router.beforeEach((to, from, next) => {
const useCounter = useCounterStore()
const nav = dataList.data.nav //获取到路由数据
// 方式1:通过pinia的confalse来判断是否是第一次加载页面,第一次的话,需要渲染路由和跳转到路径上的路由上,会执行两次
// if (useCounter.confalse) {
// routeCallback(nav)
// useCounter.$patch({
// count: nav,
// confalse: false
// })
// next({ path: to.path })
// } else {
// next() // 后面只要不刷新页面就会正常跳转路由
// }
//---------------------------------------------------------------------------------------------------------
// 方式二:首先加载路由,但是当前会多次加载路由
routeCallback(nav)
useCounter.$patch({
count: nav,
confalse: false
})
// 当前浏览页面数不少于0 或者 form的不等于 /
if (to.matched.length > 0 || from.path !== '/') {
next();
} else {
// 防止页面刷新 当上一次路由等于/ 并且 这次浏览记录为空的时候需要进入刷新前的页面
if (from.path == '/' && to.matched.length <= 0) {
next(to.path);
} else {
next('/404');
}
}
})
// 封装重复回调
function routeCallback(data: any) {
data.forEach((item: any) => {
if (item.children.length) {
routeCallback(item.children)
}
let route: any = {
name: item.name,
path: item.path,
meta: {
icon: item.icon,
title: item.title
},
component: (() => import(/* @vite-ignore */'../views/' + item.component + '.vue'))
// 如果报错了用这个 vite特别需要的引入
// component:()=> defineAsyncComponent(() => import(/* @vite-ignore */'../views/' + item.component + '.vue'))
}
router.addRoute(route)
})
}
export default router
pinia文件:stores/counter.ts
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', () => {
const count = ref({})
const confalse = ref(true)
return { count, confalse }
})
pinia文件:stores/index.ts
import { createPinia } from 'pinia'
const pinia = createPinia()
export default pinia
main.ts
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import App from './App.vue'
import router from './router'
import pinia from './stores/index'
import './assets/main.css'
import 'element-plus/dist/index.css'
const app = createApp(App)
app.use(router)
app.use(pinia)
app.use(ElementPlus)
app.mount('#app')
App.vue
<template>
<div class="app">
<div class="left">
<!-- 左侧导航栏 -->
<el-menu active-text-color="#ffd04b" background-color="#545c64" class="el-menu-vertical-demo" default-active="2" text-color="#fff" @open="handleOpen" @close="handleClose">
<el-sub-menu index="1">
<!-- 一级菜单内容 -->
<template #title>Navigator One</template>
<!-- 二级菜单 -->
<el-sub-menu index="1-1">
<!-- 二级菜单内容 -->
<template #title>item four</template>
<el-menu-item index="1-1-1">item one</el-menu-item>
</el-sub-menu>
</el-sub-menu>
<!-- 动态路由加载 -->
<el-sub-menu :index="item.name" v-for="item in count">
<template #title>{{item.title}}</template>
<router-link :to="item2.path" v-for="item2 in item.children" :key="item2.name">
<el-menu-item :index="item2.name">
<template #title>
<i :class="item2.icon"></i>
<span slot="title">{{item2.title}}</span>
</template>
</el-menu-item>
</router-link>
</el-sub-menu>
</el-menu>
</div>
<div class="right">
右侧视图
<RouterView></RouterView>
</div>
</div>
</template>
<script setup lang="ts">
import { RouterLink, RouterView, useRoute } from "vue-router"; //引入路由
import { useCounterStore } from "@/stores/counter"; //引入pinia
import { storeToRefs } from "pinia";
import {
Document,
Menu as IconMenu,
Location,
Setting,
} from "@element-plus/icons-vue";
const handleOpen = (key: string, keyPath: string[]) => {
console.log(key, keyPath);
};
const handleClose = (key: string, keyPath: string[]) => {
console.log(key, keyPath);
};
const route = useRoute();
const useCounter = useCounterStore();
const { count } = storeToRefs(useCounter);
// console.log(count);
</script>
<style scoped>
.app {
display: flex;
width: 100%;
height: 100vh;
}
.left {
width: 20%;
background-color: #545c64;
height: 100vh;
}
.right {
flex: 1;
}
</style>
数据文件:assets/data.js
export default {
"code": 200,
"msg": "操作成功",
"data": {
"nav": [
{
"id": 1,
"title": "系统管理",
"icon": "el-icon-s-operation",
"path": "",
"name": "sys:manage",
"component": "",
"children": [
{
"id": 2,
"title": "用户管理",
"icon": "el-icon-s-custom",
"path": "/sys/users",
"name": "sys:user:list",
"component": "sys/User",
"children": [
{
"id": 9,
"title": "添加用户",
"icon": null,
"path": null,
"name": "sys:user:save",
"component": null,
"children": []
},
{
"id": 10,
"title": "修改用户",
"icon": null,
"path": null,
"name": "sys:user:update",
"component": null,
"children": []
},
{
"id": 11,
"title": "删除用户",
"icon": null,
"path": null,
"name": "sys:user:delete",
"component": null,
"children": []
},
{
"id": 12,
"title": "分配角色",
"icon": null,
"path": null,
"name": "sys:user:role",
"component": null,
"children": []
},
{
"id": 13,
"title": "重置密码",
"icon": null,
"path": null,
"name": "sys:user:repass",
"component": null,
"children": []
}
]
},
{
"id": 3,
"title": "角色管理",
"icon": "el-icon-rank",
"path": "/sys/roles",
"name": "sys:role:list",
"component": "sys/Role",
"children": [
{
"id": 7,
"title": "添加角色",
"icon": "",
"path": "",
"name": "sys:role:save",
"component": "",
"children": []
},
{
"id": 14,
"title": "修改角色",
"icon": null,
"path": null,
"name": "sys:role:update",
"component": null,
"children": []
},
{
"id": 15,
"title": "删除角色",
"icon": null,
"path": null,
"name": "sys:role:delete",
"component": null,
"children": []
},
{
"id": 16,
"title": "分配权限",
"icon": null,
"path": null,
"name": "sys:role:perm",
"component": null,
"children": []
}
]
},
{
"id": 4,
"title": "菜单管理",
"icon": "el-icon-menu",
"path": "/sys/menus",
"name": "sys:menu:list",
"component": "sys/Menu",
"children": [
{
"id": 17,
"title": "添加菜单",
"icon": null,
"path": null,
"name": "sys:menu:save",
"component": null,
"children": []
},
{
"id": 18,
"title": "修改菜单",
"icon": null,
"path": null,
"name": "sys:menu:update",
"component": null,
"children": []
},
{
"id": 19,
"title": "删除菜单",
"icon": null,
"path": null,
"name": "sys:menu:delete",
"component": null,
"children": []
}
]
}
]
},
{
"id": 5,
"title": "系统工具",
"icon": "el-icon-s-tools",
"path": "",
"name": "sys:tools",
"component": null,
"children": [
{
"id": 6,
"title": "数字字典",
"icon": "el-icon-s-order",
"path": "/sys/dicts",
"name": "sys:dict:list",
"component": "sys/Dict",
"children": []
}
]
},
{
"id": 20,
"title": "管理员",
"icon": null,
"path": null,
"name": "sys:user",
"component": "sys/Normal",
"children": []
}
],
"authoritys": [
"ROLE_admin",
"ROLE_normal",
"sys:manage",
"sys:user:list",
"sys:role:list",
"sys:menu:list",
"sys:tools",
"sys:dict:list",
"sys:role:save",
"sys:user:save",
"sys:user:update",
"sys:user:delete",
"sys:user:role",
"sys:user:repass",
"sys:role:update",
"sys:role:delete",
"sys:role:perm",
"sys:menu:save",
"sys:menu:update",
"sys:menu:delete",
"sys:user"
]
}
}
以上文件复制可直接形成一个demo,视图文件自己定义一下放个template就行了