Router
import {createRouter, createWebHistory} from 'vue-router'
import other from "./other"
const router = createRouter({
// history模式:createWebHistory(), hash模式:createWebHashHistory()
history: createWebHistory(),
routes: [
{
path: '/home',
component: () => import('../view/Home.vue'),
redirect: '/home/indexShow',
children: [
/**
* 说明:
* menuLevel 为导航级别,按顺序写
*/
{
path: 'indexShow',
name: '数据概况',
menuLevel: 1,
component: () => import('../components/Index/IndexShow.vue'),
icon: 'histogram',
},
{
path: 'system',
name: '系统管理',
menuLevel: 1,
icon: 'cpu',
},
{
path: 'system/adminUser',
name: '管理员管理',
menuLevel: 2,
component: () => import('../components/system/AdminUser.vue'),
},
// --------------------------分------割-------------------------------
{
path: 'breakRouter',
menuLevel: 4,
},
...other,
]
},
// 登录页面
{
path: '/',
component: () => import('../view/Login.vue'),
},
],
})
export default router
还有非菜单的路由
/**
* 不属于菜单的路由
*/
export default [
{
path: '/trading/details',
component: () => import('../components/HelloWorld.vue')
},
]
Store
import {defineStore} from 'pinia'
export const useStore = defineStore('storeId', {
// 推荐使用 完整类型推断的箭头函数
state: () => {
return {
// 所有这些属性都将自动推断其类型
counter: 0,
name: 'Eduardo',
isAdmin: true,
}
},
// 开启数据持久化
// persist: true
})
export const sysStore = defineStore('sysStore', {
state: () => {
return {
// 菜单树
menuTree: [],
// 激活的菜单项
menuIndex: '',
// 是否折叠菜单
menuCollapse: false,
}
},
// 开启数据持久化
// persist: true
})
菜单组件
MenuComponent.vue
:
<!--菜单-->
<template>
<div class="home-menu">
<el-menu
:default-active="sysStoreData.menuIndex"
router
background-color="#304156"
text-color="#ddd"
active-text-color="#409eff"
:collapse="sysStoreData.menuCollapse">
<MenuTree :menuList="sysStoreData.menuTree"></MenuTree>
</el-menu>
</div>
</template>
<script setup>
import router from '../../router'
import MenuTree from './MenuTree.vue'
import {sysStore} from '../../store'
const sysStoreData = sysStore()
menu()
// 菜单计算(将路由转为为菜单树)
function menu() {
// 获取所有菜单列表
let menus = router.options.routes[0].children
let oneItem = {}
let twoItem = {}
let oneMenu = []
let twoMenu = []
let threeMenu = []
for (let item of menus) {
// 分割
if (item.menuLevel === 4) {
break
}
item.path = '/home/' + item.path
if (item.menuLevel === 1) {
if (threeMenu.length > 0) {
twoItem.ch = threeMenu
twoMenu.push(twoItem)
twoItem = {}
threeMenu = []
}
if (twoMenu.length > 0) {
oneItem.ch = twoMenu
oneMenu.push(oneItem)
oneItem = {}
twoMenu = []
}
if (item.component) {
// 该级别可点击,校验是否有权限
menuShowRule(item, () => {
// 没有下级,直接添加进最后数据
oneMenu.push(item)
})
} else {
// 不可点击
oneItem = item
}
} else if (item.menuLevel === 2) {
if (threeMenu.length > 0) {
twoItem.ch = threeMenu
twoMenu.push(twoItem)
twoItem = {}
threeMenu = []
}
if (item.component) {
// 该级别可点击,校验是否有权限
menuShowRule(item, () => {
// 没有下级,直接添加进数据
twoMenu.push(item)
})
} else {
// 不可点击
twoItem = item
}
twoItem = item
} else if (item.menuLevel === 3) {
menuShowRule(item, () => {
threeMenu.push(item)
})
}
}
if (threeMenu.length > 0) {
twoItem.ch = threeMenu
twoMenu.push(twoItem)
}
if (twoMenu.length > 0) {
oneItem.ch = twoMenu
oneMenu.push(oneItem)
}
// 保存菜单
sysStoreData.menuTree = oneMenu
console.log(oneMenu)
// 首次展示
if (sysStoreData.menuIndex === '') {
sysStoreData.menuIndex = oneMenu[0].path
}
}
/**
* 定义是否显示的规则
*/
function menuShowRule(item, fun) {
fun()
}
</script>
<style scoped lang="sass">
// 菜单高度
.el-menu-item
height: 45px
.el-sub-menu
height: 45px
// 去除菜单边框
.el-menu
border-right: none
</style>
MenuTree.vue
:
<template>
<template v-for="menu in menuList">
<!--没有子菜单-->
<el-menu-item v-if="menu.component" :index="menu.path" @click="sysStoreData.menuIndex = menu.path">
<el-icon>
<component :is="menu.icon"></component>
</el-icon>
<span>{{ menu.name }}</span>
</el-menu-item>
<!--有子菜单-->
<el-sub-menu
v-else
:index="menu.path">
<template #title>
<el-icon>
<component :is="menu.icon"></component>
</el-icon>
<span>{{ menu.name }}</span>
</template>
<!--递归调用自身-->
<MenuTree :menuList="menu.ch"></MenuTree>
</el-sub-menu>
</template>
</template>
<script setup>
import {sysStore} from '../../store'
const sysStoreData = sysStore()
defineProps({
menuList: {
type: Array,
required: false,
}
})
</script>
<style scoped>
</style>
菜单升级和BUG修复
出现问题:无法支持4级菜单,所以进行了升级,同时上面方式有个BUG,当菜单中出现相同名字时,无法进行跳转,是由于router的name不能重复造成,以下是升级和修复后的代码
router
import {createRouter, createWebHashHistory} from 'vue-router'
import other from "./other"
import api from '../http/api.js'
import {sysStore, adminStore} from '../store'
const router = createRouter({
// history模式:createWebHistory(), hash模式:createWebHashHistory()
history: createWebHashHistory(),
routes: [
{
path: '/home',
component: () => import('../view/Home.vue'),
redirect: '/home/indexShow',
children: [
/**
* 说明:
* menuLevel 为导航级别,按顺序写
*
* 说明:
* path 路径
* name 路由名,不可重复,与path一致
* menuName 菜单名
* menuLevel 导航级别,从上往下按顺序写
* component 页面组件
* icon 图标,仅一级导航有做显示
* auth 权限
* ch 组件内按钮权限,用在授权处
*/
{
path: 'indexShow',
name: 'indexShow',
menuName: '数据概况',
menuLevel: 1,
component: () => import('../components/index/IndexShow.vue'),
icon: 'histogram',
},
{
path: 'system',
name: 'system',
menuName: '系统管理',
menuLevel: 1,
icon: 'cpu',
},
{
path: 'system/adminUser',
name: 'system/adminUser',
menuName: '管理员管理',
menuLevel: 2,
component: () => import('../components/system/AdminUser.vue'),
auth: api.system_admin_user_list,
ch: [
{name: '添加管理员', auth: api.system_admin_user_add},
{name: '编辑管理员', auth: api.system_admin_user_edit},
{name: '删除管理员', auth: api.system_admin_user_del},
{name: '密码重置', auth: api.system_admin_reset_password},
],
},
{
path: 'system/adminRole',
name: 'system/adminRole',
menuName: '管理员角色及权限',
menuLevel: 2,
component: () => import('../components/system/AdminRole.vue'),
auth: api.system_admin_role_list,
ch: [
{name: '添加角色', auth: api.system_admin_role_add},
{name: '编辑角色', auth: api.system_admin_role_edit},
{name: '删除角色', auth: api.system_admin_role_del},
],
},
{
path: 'system/adminUserLog',
name: 'system/adminUserLog',
menuName: '管理员日志',
menuLevel: 2,
component: () => import('../components/system/AdminLog.vue'),
auth: api.system_admin_log_list,
},
{
path: 'reagent',
name: 'reagent',
menuName: '试剂耗材管理',
menuLevel: 1,
icon: 'Tickets',
},
{
path: 'reagentCommon',
name: 'reagentCommon',
menuName: '普通试剂',
menuLevel: 2,
},
{
path: 'reagentCommonPut',
name: 'reagentCommonPut',
menuName: '管理人员入库',
menuLevel: 3,
component: () => import('../components/reagent/ReagentPutRecord.vue'),
auth: api.system_admin_user_list,
ch: [
{name: '添加', auth: api.system_admin_user_add},
{name: '编辑管理员', auth: api.system_admin_user_edit},
{name: '删除管理员', auth: api.system_admin_user_del},
{name: '密码重置', auth: api.system_admin_reset_password},
],
},
{
path: 'chemistry',
name: 'chemistry',
menuName: '危险化学品',
menuLevel: 2,
},
{
path: 'chemistryPoison',
name: 'chemistryPoison',
menuName: '易制毒',
menuLevel: 3,
},
{
path: 'chemistryPoisonPut',
name: 'chemistryPoisonPut',
menuName: '管理人员入库',
menuLevel: 4,
component: () => import('../components/reagent/ReagentPutRecord2.vue'),
auth: api.system_admin_user_list,
ch: [
{name: '添加', auth: api.system_admin_user_add},
{name: '编辑管理员', auth: api.system_admin_user_edit},
{name: '删除管理员', auth: api.system_admin_user_del},
{name: '密码重置', auth: api.system_admin_reset_password},
],
},
// --------------------------分------割-------------------------------
{
path: 'breakRouter',
menuLevel: 5,
},
...other,
]
},
{
path: '/',
component: () => import('../view/Login.vue'),
},
],
})
router.beforeEach((to, from, next) => {
let sysStoreData = sysStore()
let adminStoreData = adminStore()
if (to.path === '/' || adminStoreData.token.length > 0) {
next()
} else {
next('/')
}
})
export default router
store
import {defineStore} from 'pinia'
export const useStore = defineStore('storeId', {
// 推荐使用 完整类型推断的箭头函数
state: () => {
return {
// 所有这些属性都将自动推断其类型
counter: 0,
name: 'Eduardo',
isAdmin: true,
}
},
// 开启数据持久化
persist: true
})
// 动态菜单
export const sysStore = defineStore('sysStore', {
state: () => {
return {
// 菜单树
menuTree: [],
// 激活的菜单项
menuIndex: '',
// 是否折叠菜单
menuCollapse: false,
}
},
// 开启数据持久化
persist: true
})
// 动态菜单
export const adminStore = defineStore('adminStore', {
state: () => {
return {
// 管理员ID
id: '',
// 登录Token
token: '',
// 权限值
auth: [],
// 昵称
nick: '',
// 账号类型:1-超级管理员、2-普通管理员
type: '',
// 账号级别
level: '',
// 是否超级管理员
superAdmin: false,
}
},
// 开启数据持久化
persist: true
})
组件MenuComponent.vue
<!--菜单-->
<template>
<div class="home-menu">
<el-menu
:default-active="sysStoreData.menuIndex"
router
background-color="#304156"
text-color="#ddd"
active-text-color="#409eff"
:collapse="sysStoreData.menuCollapse">
<MenuTree :menuList="sysStoreData.menuTree"></MenuTree>
</el-menu>
</div>
</template>
<script setup>
import router from '../../router'
import MenuTree from './MenuTree.vue'
import {sysStore} from '../../store'
const sysStoreData = sysStore()
menu()
// 菜单计算(将路由转为为菜单树)
function menu() {
// 获取所有菜单列表
let menus = router.options.routes[0].children
let oneItem = {}
let twoItem = {}
let threeItem = {}
let oneMenu = []
let twoMenu = []
let threeMenu = []
let fourMenu = []
// 遍历菜单
for (let item of menus) {
// 分割
if (item.menuLevel === 5) {
break
}
item.path = '/home/' + item.path
if (item.menuLevel === 1) {
if (fourMenu.length > 0) {
threeItem.ch = fourMenu
threeMenu.push(threeItem)
threeItem = {}
fourMenu = []
}
if (threeMenu.length > 0) {
twoItem.ch = threeMenu
twoMenu.push(twoItem)
twoItem = {}
threeMenu = []
}
if (twoMenu.length > 0) {
oneItem.ch = twoMenu
oneMenu.push(oneItem)
oneItem = {}
twoMenu = []
}
if (item.component) {
// 该级别可点击,校验是否有权限
menuShowRule(item, () => {
// 没有下级,直接添加进最后数据
oneMenu.push(item)
})
} else {
// 不可点击,属于目录
oneItem = item
}
} else if (item.menuLevel === 2) {
if (fourMenu.length > 0) {
threeItem.ch = fourMenu
threeMenu.push(threeItem)
threeItem = {}
fourMenu = []
}
if (threeMenu.length > 0) {
twoItem.ch = threeMenu
twoMenu.push(twoItem)
twoItem = {}
threeMenu = []
}
if (item.component) {
// 该级别可点击,校验是否有权限
menuShowRule(item, () => {
// 没有下级,直接添加进数据
twoMenu.push(item)
})
} else {
// 不可点击,属于目录
twoItem = item
}
} else if (item.menuLevel === 3) {
if (fourMenu.length > 0) {
threeItem.ch = fourMenu
threeMenu.push(threeItem)
threeItem = {}
fourMenu = []
}
if (item.component) {
// 该级别可点击,校验是否有权限
menuShowRule(item, () => {
// 没有下级,直接添加进数据
threeMenu.push(item)
})
} else {
// 不可点击,属于目录
threeItem = item
}
} else if (item.menuLevel === 4) {
menuShowRule(item, () => {
fourMenu.push(item)
})
}
}
if (fourMenu.length > 0) {
threeItem.ch = fourMenu
threeMenu.push(threeItem)
}
if (threeMenu.length > 0) {
twoItem.ch = threeMenu
twoMenu.push(twoItem)
}
if (twoMenu.length > 0) {
oneItem.ch = twoMenu
oneMenu.push(oneItem)
}
// 保存菜单
sysStoreData.menuTree = oneMenu
console.log(oneMenu)
// 首次展示
if (sysStoreData.menuIndex === '') {
sysStoreData.menuIndex = oneMenu[0].path
}
}
/**
* 定义是否显示的规则
*/
function menuShowRule(item, fun) {
fun()
}
</script>
<style scoped lang="sass">
// 菜单高度
.el-menu-item
height: 45px
.el-sub-menu
height: 45px
// 去除菜单边框
.el-menu
border-right: none
</style>
组件MenuTree.vue
<template>
<template v-for="menu in menuList">
<!--没有子菜单-->
<el-menu-item v-if="menu.component" :index="menu.path" @click="sysStoreData.menuIndex = menu.path">
<el-icon>
<component :is="menu.icon"></component>
</el-icon>
<span>{{ menu.menuName }}</span>
</el-menu-item>
<!--有子菜单-->
<el-sub-menu
v-else
:index="menu.path">
<template #title>
<el-icon>
<component :is="menu.icon"></component>
</el-icon>
<span>{{ menu.menuName }}</span>
</template>
<!--递归调用自身-->
<MenuTree :menuList="menu.ch"></MenuTree>
</el-sub-menu>
</template>
</template>
<script setup>
import {sysStore} from '../../store'
const sysStoreData = sysStore()
defineProps({
menuList: {
type: Array,
required: false,
}
})
</script>
<style scoped>
</style>