后台管理
持续更新中。。。
1. 动态路由
前端路由配置信息
export const constantRoutes = [
{
path: '/login',
component: () => import('../views/login/index.vue'),
name: 'login',
meta: {
label: '登录页面',
icon: '',
hidden: true,
}
},
{
path: '/404',
component: () => import('@/views/404/index.vue'),
name: '404',
meta: {
label: '404',
icon: '',
hidden: true
},
},
]
export const asyncRoutes = [
{
path: '/',
name: 'Layout',
redirect: '/home',
component: () => import('@/layout/index.vue'),
meta: {
label: '',
icon: '',
hidden: false,
},
children: [
{
path: '/home',
name: 'Home',
component: () => import('@/views/home/index.vue'),
meta: {
label: '首页',
icon: 'HomeFilled',
hidden: false
}
},
]
},
{
path: '/perm',
name: 'Perm',
component: () => import('@/layout/index.vue'),
redirect: '/perm/user',
meta: {
label: '权限管理',
icon: 'Lock',
hidden: false
},
children: [
{
path: '/perm/user',
name: 'User',
component: () => import('@/views/perm/user/index.vue'),
meta: {
label: '用户管理',
icon: 'User',
hidden: false
},
},
{
path: '/perm/role',
name: 'Role',
component: () => import('@/views/perm/role/index.vue'),
meta: {
label: '角色管理',
icon: 'Avatar',
hidden: false
},
},
{
path: '/perm/menu',
name: 'Menu',
component: () => import('@/views/perm/menu/index.vue'),
meta: {
label: '菜单管理',
icon: 'Grid',
hidden: false
},
},
]
}
]
export const anyRoutes = [
{
path: '/:pathMatcher(.*)*',
redirect: '/404',
name: 'Any',
meta: {
label: '',
icon: '',
hidden: true
}
},
]
- 获取token后,请求后端接口获取用户信息
{
code: 200,
message: '操作成功',
data: {
username: 'wuyu',
roles: ['admin'],
avatar: '/public/vite.svg',
routes: ['Layout', 'Home', 'Screen', 'Perm', 'User', 'Role', 'Menu', 'Product', 'Attr', 'Sku', 'Spu', 'Trademark', 'Any'],
buttons: ['sys:user:add','sys:user:delete','sys:user:insert','sys:user:select']
}
}
- 通过 路由唯一标识符Name 从前端的动态路由中过滤出我们需要的路由
import {anyRoutes, asyncRoutes, constantRoutes} from "@/router/routes.js"; // 导入前端路由
import {cloneDeep} from "lodash/lang.js"; // 第三方包,用于深拷贝
// 过滤出来异步路由
function filterAsyncRoutes(asyncRoutes, routes) {
return asyncRoutes.filter(item => {
if (routes.includes(item.name)) {
if (item.children && item.children.length > 0) {
item.children = filterAsyncRoutes(item.children, routes)
}
return true
}
})
}
export const useUserStore = defineStore('user', () => {
const userInfo = ref(null)
const getUserInfo = async () => {
userInfo.value = await userInfoService()
const filteredAsyncRoutes = filterAsyncRoutes(cloneDeep(asyncRoutes), userInfo.value.routes)
userInfo.value.routes = [...filteredAsyncRoutes, ...constantRoutes, ...anyRoutes]
userInfo.value.routes.forEach(item => {
router.addRoute(item)
})
}
return {
userInfo,
getUserInfo
}
},
)
- 路由守卫
router.beforeEach(async (to, from, next) => {
const userStore = useUserStore()
// 判断用户是否登录
if (userStore.token) {
if (userStore.userInfo) {
// 已经登录,访问/login,会重定向到 / (首页)
if (to.path === '/login') {
next('/home')
} else {
next()
}
} else {
await userStore.getUserInfo()
next({...to, replace: true})
}
} else {
// 未登录
if (to.path === '/login') {
next()
} else {
// 重定向到/login,并追加一个 登录成功后重定向地址
next({path: '/login', query: {redirect: to.path}})
}
}
})
2. 动态侧边栏菜单
- 获取动态菜单路由数据
// 获取用户路由
<script setup>
const userStore = useUserStore()
const routes = userStore.userInfo.routes
// 去 hidden 函数
// 如果父路由 隐藏,那么子路由hidden不论true,false,都不会显示
// 如果父路由 显示,那么子路由hidden为false会显示,hidden为true会隐藏
const filterHiddenRoute = (routes) => {
return routes.filter(item => {
if (!item.meta.hidden) {
if (item.children) {
item.children = filterHiddenRoute(item.children)
}
return true
} else {
return false
}
})
}
// 去掉hidden后的菜单树
const menuList = computed(() => {
return filterHiddenRoute(routes)
})
</script>
<template>
<el-menu>
<aside-menu :menuList="menuList"></aside-menu>
</el-menu>
</template>
-
动态菜单递归组件定义
递归菜单必须有组件名,用于在组件中调用自身
<script setup>
import router from '@/router'
defineOptions({name: 'AsideMenu'})
const menus = defineProps(['menuList'])
// 跳转路由
const goRoute = (e) => {
router.push(e.index)
}
</script>
<template>
<template v-for="item of menus.menuList" :key="item.path">
<!-- 没有子菜单-->
<el-menu-item v-if="!item.children" :index="item.path" @click="goRoute">
<el-icon>
<component :is="item.meta.icon"></component>
</el-icon>
<template #title>
<span>{{ item.meta.label }}</span>
</template>
</el-menu-item>
<!-- 有子菜单,但是只有1个-->
<el-menu-item v-if="item.children && item.children.length===1" :index="item.children[0].path" @click="goRoute">
<el-icon>
<component :is="item.children[0].meta.icon"></component>
</el-icon>
<template #title>
<span>{{ item.children[0].meta.label }}</span>
</template>
</el-menu-item>
<!-- 有子菜单,子菜单大于1个-->
<el-sub-menu v-if="item.children && item.children.length>1" :index="item.path">
<template #title>
<el-icon>
<component :is="item.meta.icon"></component>
</el-icon>
<span>{{ item.meta.label }}</span>
</template>
<AsideMenu :menuList="item.children"></AsideMenu>
</el-sub-menu>
</template>
</template>
<style scoped lang="scss">
</style>