主要有动态路由添加,按钮级权限,无限菜单等功能,写的很简略
地址:https://www.aliyundrive.com/s/ZDPUMQPhjc5
安装下依赖(yarn 或 npm i),直接yarn dev 或者 npm run dev 即可运行
分了两个角色,admin和vip
首先我自己模拟的后台json数据
这个是管理员的,有子菜单的就不写component
let userInfo = {
name: '坤坤',
token: 'token XXX',
role: 'admin',
menu: [
{
path: '/about',
name: 'about',
children: [
{
path: '/dance',
name: 'dance',
component: 'Dance'
},
{
path: '/rap',
name: 'rap',
children: [
{
path: '/basketball',
name: 'basketball',
children: [
{
path: '/weiweiwei',
name: 'weiweiwei',
component: 'Test'
},
{
path: '/weiweiwei2',
name: 'weiweiwei2',
component: 'Test'
},
]
}
]
}
]
},
{
path: '/sing',
name: 'sing',
component: 'Sing'
}
]
}
export default userInfo
这个是vip的,有子菜单的就不写component
let userInfo = {
name: '张三',
token: 'token XXX',
role: 'vip',
menu: [
{
path: '/about',
name: 'about',
component: 'About'
},
{
path: '/sing',
name: 'sing',
component: 'Sing'
}
]
}
export default userInfo
路由配置(配置了共有的页面,所有的动态路由,都添加到home的children里面)
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: HomeView,
redirect: '/sing',
children: [
{
path: '/sing',
name: 'sing',
component: () => import('../views/Sing.vue')
}
]
},
{
path: '/login',
name: 'login',
component: () => import('../views/Login.vue')
},
{
path: '/:pathMatch(.*)',
name: 'notfound',
component: () => import('../views/NotFound/index.vue')
}
]
})
// 处理菜单,一会动态添加路由用
function getMenu(menu) {
let newMenu = menu?.map(item => {
return {
path: item.path,
name: item.name,
component: item.component ? () => import(`../views/${item.component}.vue`) : '',
children: getMenu(item.children)
}
})
return newMenu
}
let newMenu = getMenu(JSON.parse(localStorage.getItem('userInfo'))?.menu)
newMenu?.forEach(item => {
router.addRoute('home', item)
})
router.beforeEach((to, from, next) => {
if (to.path === '/login') {
next()
} else {
let token = localStorage.getItem('token')
if (!token) {
next('/login')
} else {
// 只有从login页面过来的,才执行下面操作(不然每次路由跳转都执行,不太好)
if (from.path === '/login') {
// 从本地拿数据,并动态添加到路由上
let newMenu = getMenu(JSON.parse(localStorage.getItem('userInfo'))?.menu)
newMenu?.forEach(item => {
router.addRoute('home', item)
})
}
next()
}
}
})
export default router
登录页(把个人信息和token全存到本地中)
<template>
<div class="login">
<el-form :model="form" label-width="60px">
<el-form-item label="用户名">
<el-input v-model="form.name" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">登录</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script setup>
import { reactive } from 'vue'
import kunkun from '../data/kunkun'
import zhangsan from '../data/zhangsan'
import { useRouter } from 'vue-router'
const userouter = useRouter()
const form = reactive({
name: '',
})
const onSubmit = () => {
if (form.name === '坤坤') {
localStorage.setItem('userInfo',JSON.stringify(kunkun))
localStorage.setItem('token',kunkun.token)
userouter.push('/')
}
if (form.name === '张三') {
localStorage.setItem('userInfo',JSON.stringify(zhangsan))
localStorage.setItem('token',zhangsan.token)
userouter.push('/')
}
}
</script>
<style lang="scss" scoped>
.login {
width: 300px;
height: 100px;
background-color: pink;
margin: 10px auto;
}
</style>
首页(向循环组件传递路由数据)
<template>
<div class="common-layout">
<el-container>
<el-aside width="200px">
<MenuTree :MenuData="MenuData"></MenuTree>
</el-aside>
<el-container>
<el-header>
<span>Header</span>
<div>
<span>{
{ uname }} --- {
{ role }}</span>
<el-button @click="goback">退出</el-button>
</div>
</el-header>
<el-main><RouterView /></el-main>
</el-container>
</el-container>
</div>
</template>
<script setup>
import { useRouter } from "vue-router";
import MenuTree from "../components/MenuTree.vue";
const userouter = useRouter();
let MenuData = JSON.parse(localStorage.getItem("userInfo"))?.menu;
let uname = JSON.parse(localStorage.getItem("userInfo"))?.name;
let role = JSON.parse(localStorage.getItem("userInfo"))?.role;
const goback = () => {
localStorage.clear();
userouter.replace("/login");
};
</script>
<style scoped lang="scss">
.el-aside {
height: 100vh;
background-color: pink;
}
.el-header {
display: flex;
justify-content: space-between;
background-color: skyblue;
}
</style>
无限菜单组件(如果有children,就把数据传输,再调用自身)
<template>
<el-menu
:default-active="useroute.fullPath"
v-for="item in MenuData"
:key="item.path"
router
>
<el-menu-item :index="item.path" v-if="!item.children">
<span>{
{ item.name }}</span>
</el-menu-item>
<el-sub-menu :index="item.path" v-else>
<template #title>
<span>{
{ item.name }}</span>
</template>
<MenuTree :MenuData="item?.children" />
</el-sub-menu>
</el-menu>
</template>
<script setup>
import { defineProps } from "vue";
import { useRoute } from "vue-router";
const useroute = useRoute();
let { MenuData } = defineProps({
MenuData: {
default: [],
type: Array,
required: true,
},
})
</script>
首页展示的二级菜单(根据角色渲染标签,就实现按钮级的权限)
<template>
<h2>sing</h2>
<el-button v-if="role === 'vip'">vip才显示</el-button>
<el-button v-if="role === 'admin'">admin才显示</el-button>
</template>
<script setup>
let role = JSON.parse(localStorage.getItem('userInfo'))?.role
</script>
<style scoped>
</style>