From 8a24e76d002c8ef3add059d0436c9fcf22de5003 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=98chen=2Ehome=E2=80=99?= <1147347984@qq.com> Date: Thu, 18 Aug 2022 00:17:45 +0800 Subject: [PATCH] =?UTF-8?q?feat(menu):=20=E4=BF=AE=E6=94=B9=E8=8F=9C?= =?UTF-8?q?=E5=8D=95=E9=AB=98=E4=BA=AE=E8=87=AA=E5=8A=A8=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mock/module/user.ts | 97 +++++--------------- src/layouts/components/header/Breadcrumb.vue | 3 + src/layouts/components/sider/Logo.vue | 7 +- src/layouts/components/sider/Menu.vue | 4 +- src/router/guard/dynamic.ts | 4 + src/router/guard/index.ts | 2 +- src/router/guard/permission.ts | 10 +- src/store/modules/route.ts | 74 +++++++++++---- src/types/business.d.ts | 8 -- src/views/test/test2/detail/index.vue | 7 ++ src/views/test/test3/test4/index.vue | 14 +++ 11 files changed, 125 insertions(+), 105 deletions(-) create mode 100644 src/views/test/test2/detail/index.vue create mode 100644 src/views/test/test3/test4/index.vue diff --git a/mock/module/user.ts b/mock/module/user.ts index 8b06d59..dec678d 100644 --- a/mock/module/user.ts +++ b/mock/module/user.ts @@ -5,79 +5,6 @@ const Random = Mock.Random; const token = Random.string('upper', 32, 32); -const routeData = { - admin: [ - { - name: 'dashboard', - path: '/dashboard', - meta: { - title: '分析页', - requiresAuth: true, - icon: 'icon-park-outline:analysis', - }, - children: [ - { - name: 'dashboard_workbench', - path: '/dashboard/workbench', - meta: { - title: '工作台', - requiresAuth: true, - icon: 'icon-park-outline:alarm', - }, - }, - { - name: 'dashboard_monitor', - path: '/dashboard/monitor', - meta: { - title: '监控页', - requiresAuth: true, - icon: 'icon-park-outline:anchor', - }, - }, - ], - }, - { - name: 'test', - path: '/test', - meta: { - title: '测试专题', - requiresAuth: true, - icon: 'icon-park-outline:ambulance', - }, - children: [ - { - name: 'test1', - path: '/test/test1', - meta: { - title: '测试专题1', - requiresAuth: true, - icon: 'icon-park-outline:alarm', - }, - }, - { - name: 'test2', - path: '/test/test2', - meta: { - title: '测试专题2', - requiresAuth: true, - icon: 'icon-park-outline:pic', - }, - }, - { - name: 'test3', - path: '/test/test3', - meta: { - title: '测试专题3', - requiresAuth: true, - icon: 'icon-park-outline:tool', - }, - }, - ], - }, - ], - user: [], -}; - const userInfo = { userId: '1', userName: 'admin', @@ -144,6 +71,19 @@ const userRoutes = [ requiresAuth: true, icon: 'icon-park-outline:pic', }, + children: [ + { + name: 'test2_detail', + path: '/test/test2/detail', + meta: { + title: '测试专题2的详情页', + requiresAuth: true, + icon: 'icon-park-outline:tool', + hide: true, + activeMenu: '/test/test2', + }, + }, + ], }, { name: 'test3', @@ -153,6 +93,17 @@ const userRoutes = [ requiresAuth: true, icon: 'icon-park-outline:tool', }, + children: [ + { + name: 'test4', + path: '/test/test3/test4', + meta: { + title: '测试专题4', + requiresAuth: true, + icon: 'icon-park-outline:tool', + }, + }, + ], }, ], }, diff --git a/src/layouts/components/header/Breadcrumb.vue b/src/layouts/components/header/Breadcrumb.vue index 5891b32..fd75e58 100644 --- a/src/layouts/components/header/Breadcrumb.vue +++ b/src/layouts/components/header/Breadcrumb.vue @@ -10,9 +10,12 @@ diff --git a/src/layouts/components/sider/Menu.vue b/src/layouts/components/sider/Menu.vue index c7e176e..b000506 100644 --- a/src/layouts/components/sider/Menu.vue +++ b/src/layouts/components/sider/Menu.vue @@ -5,6 +5,7 @@ :collapsed-icon-size="24" :indent="20" :options="routesStore.menus" + :value="routesStore.activeMenu" @update:value="handleClickMenu" /> @@ -13,12 +14,13 @@ import { useAppStore } from '@/store'; import { useAppRouter } from '@/hook'; import { useRouteStore } from '~/src/store/modules/route'; +import type { MenuOption } from 'naive-ui'; const { routerPush } = useAppRouter(); const appStore = useAppStore(); const routesStore = useRouteStore(); -const handleClickMenu = (key: string) => { +const handleClickMenu = (key: string, item: MenuOption) => { routerPush(key); }; diff --git a/src/router/guard/dynamic.ts b/src/router/guard/dynamic.ts index 644837e..c82f708 100644 --- a/src/router/guard/dynamic.ts +++ b/src/router/guard/dynamic.ts @@ -37,6 +37,10 @@ export async function createDynamicRoutes(routes: AppRoute.Route[]) { name: 'appRoot', redirect: '/dashboard/workbench', component: BasicLayout, + meta: { + title: '首页', + icon: 'icon-park-outline:home', + }, children: [], }; // 根据角色过滤后的插入根路由中 diff --git a/src/router/guard/index.ts b/src/router/guard/index.ts index 0beed5d..3c7f3ab 100644 --- a/src/router/guard/index.ts +++ b/src/router/guard/index.ts @@ -13,7 +13,7 @@ export function setupRouterGuard(router: Router) { }); router.afterEach((to) => { // 修改网页标题 - document.title = `${to.meta.title}——${appTitle}`; + document.title = `${to.meta.title} — ${appTitle}`; // 结束 loadingBar window.$loadingBar?.finish(); }); diff --git a/src/router/guard/permission.ts b/src/router/guard/permission.ts index d9b5208..90c9003 100644 --- a/src/router/guard/permission.ts +++ b/src/router/guard/permission.ts @@ -1,7 +1,6 @@ import { RouteLocationNormalized, NavigationGuardNext } from 'vue-router'; import { getToken } from '@/utils/auth'; import { useRouteStore } from '@/store'; -// import { setDynamicRoutes } from './dynamic'; export async function createPermissionGuard( to: RouteLocationNormalized, @@ -26,7 +25,6 @@ export async function createPermissionGuard( return false; } // 有登录但是没有路由,初始化路由、侧边菜单等 - // await setDynamicRoutes(); await routeStore.initAuthRoute(); // 动态路由加载完回到根路由 next({ name: 'root' }); @@ -37,6 +35,12 @@ export async function createPermissionGuard( // next({ name: 'not-found-page', replace: true }); // } - // next({ name: 'root' }); + // 设置菜单高亮 + if (to.meta.activeMenu) { + routeStore.setActiveMenu(to.meta.activeMenu); + } else { + routeStore.setActiveMenu(to.fullPath); + } + next(); } diff --git a/src/store/modules/route.ts b/src/store/modules/route.ts index 9cbb6d5..bb6f452 100644 --- a/src/store/modules/route.ts +++ b/src/store/modules/route.ts @@ -8,7 +8,8 @@ import { fetchUserRoutes } from '@/service'; interface RoutesStatus { isInitAuthRoute: boolean; menus: any; - userRoutes: any; + userRoutes: AppRoute.Route[]; + activeMenu: string | null; } export const useRouteStore = defineStore('route-store', { state: (): RoutesStatus => { @@ -16,6 +17,7 @@ export const useRouteStore = defineStore('route-store', { userRoutes: [], isInitAuthRoute: false, menus: [], + activeMenu: null, }; }, actions: { @@ -27,10 +29,38 @@ export const useRouteStore = defineStore('route-store', { /* 删除后面添加的路由 */ router.removeRoute('appRoot'); }, + /* 根据当前路由的name生成面包屑数据 */ + createBreadcrumbInRoutes(name = '/', userRoutes: AppRoute.Route[]) { + return userRoutes.filter((item) => { + if (item.name === name) { + return true; + } + if (item.children) { + return this.hasNameInBreadcrumbsChildren(name, item.children); + } + }); + }, + /* 判断子路由中是否存在为name的路由 */ + hasNameInBreadcrumbsChildren(name: string, userRoutes: AppRoute.Route[]): boolean { + return userRoutes.some((item) => { + if (item.name === name) { + return true; + } + if (item.children) { + return this.hasNameInBreadcrumbsChildren(name, item.children); + } + }); + }, + /* 设置当前高亮的菜单key */ + setActiveMenu(key: string) { + this.activeMenu = key; + }, + /* 生成侧边菜单的数据 */ createMenus(userRoutes: AppRoute.Route[]) { this.userRoutes = userRoutes; this.menus = this.transformAuthRoutesToMenus(userRoutes); }, + /* 初始化动态路由 */ async initDynamicRoute() { // 根据用户id来获取用户的路由 const { userId } = getUserInfo(); @@ -42,26 +72,34 @@ export const useRouteStore = defineStore('route-store', { // 插入路由表 router.addRoute(appRoutes); }, - // 将返回的路由表渲染成侧边栏 + //* 将返回的路由表渲染成侧边栏 */ transformAuthRoutesToMenus(userRoutes: AppRoute.Route[]): MenuOption[] { - return userRoutes.map((item) => { - const target: MenuOption = { - label: item.meta.title, - key: item.path, - }; - // 判断有无图标 - if (item.meta.icon) { - target.icon = renderIcon(item.meta.icon); - } - // 判断子元素 - if (item.children) { - target.children = this.transformAuthRoutesToMenus(item.children); - } - return target; - }); + return userRoutes + .filter((item) => { + return !item.meta.hide; + }) + .map((item) => { + const target: MenuOption = { + label: item.meta.title, + key: item.path, + }; + // 判断有无图标 + if (item.meta.icon) { + target.icon = renderIcon(item.meta.icon); + } + // 判断子元素 + if (item.children) { + const children = this.transformAuthRoutesToMenus(item.children); + // 只有子元素有且不为空时才添加 + if (children.length !== 0) { + target.children = children; + } + } + return target; + }); }, - async initAuthRoute() { + this.isInitAuthRoute = false; await this.initDynamicRoute(); this.isInitAuthRoute = true; }, diff --git a/src/types/business.d.ts b/src/types/business.d.ts index d305023..865f40c 100644 --- a/src/types/business.d.ts +++ b/src/types/business.d.ts @@ -7,7 +7,6 @@ declare namespace Auth { * - user: 用户 * - custom: 自定义角色 */ - // type RoleType = keyof typeof import('@/enum').EnumUserRole; /** 用户信息 */ interface loginToken { @@ -28,11 +27,4 @@ declare namespace Auth { /* 密码 */ password: string; } - // interface userRoutes { - // name: string; - // path: string; - // meta: AppRoute.RouteMeta; - // children?: userRoutes[]; - // redirect: string; - // } } diff --git a/src/views/test/test2/detail/index.vue b/src/views/test/test2/detail/index.vue new file mode 100644 index 0000000..e639079 --- /dev/null +++ b/src/views/test/test2/detail/index.vue @@ -0,0 +1,7 @@ + + + + + diff --git a/src/views/test/test3/test4/index.vue b/src/views/test/test3/test4/index.vue new file mode 100644 index 0000000..417ae8e --- /dev/null +++ b/src/views/test/test3/test4/index.vue @@ -0,0 +1,14 @@ + + + + +