Skip to content

Commit

Permalink
refactor: 重构路由, 增加动态路由功能、菜单嵌套功能
Browse files Browse the repository at this point in the history
  • Loading branch information
Yolo-00 committed Oct 31, 2024
1 parent df5239e commit eea8173
Show file tree
Hide file tree
Showing 16 changed files with 332 additions and 126 deletions.
20 changes: 13 additions & 7 deletions src/layouts/components/AsideMenu/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { computed } from "vue";
// pinia
import { useAppStore } from "@/stores/modules/app";
// vue-router
import { useRouter, useRoute, type RouteRecordRaw } from "vue-router";
import { useRoute } from "vue-router";
import type { CustomRouteRecordRaw } from "@/routers/interface/index";
// component
import SubMenuItem from "../SubMenuItem/index.vue";
// vueuse
Expand All @@ -13,17 +14,22 @@ const title = import.meta.env.VITE_APP_TITLE;
const { width } = useWindowSize();
const appStore = useAppStore();
const router = useRouter();
const route = useRoute();
const routerList = computed<RouteRecordRaw[]>(() => {
let list = router.options.routes.map((item: RouteRecordRaw) => (item.children?.length === 1 ? item.children[0] : item));
list = list.filter(item => !item.meta?.hidden);
return list;
});
const routerList = computed<CustomRouteRecordRaw[]>(() => appStore.menuList);
// 处理面包屑
const getAllBreadcrumbList = (menuList: CustomRouteRecordRaw[], parent = [], result: { [key: string]: any } = {}) => {
for (const item of menuList) {
result[item.path] = [...parent, item];
if (item.children) getAllBreadcrumbList(item.children, result[item.path], result);
}
return result;
};
const activeMenu = computed(() => {
const { path } = route;
appStore.setCrumbsList(getAllBreadcrumbList(appStore.menuList)[route.matched[route.matched.length - 1].path]);
return path;
});
</script>
Expand Down
7 changes: 3 additions & 4 deletions src/layouts/components/SubMenuItem/index.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
<script setup lang="ts" name="SubMenuItem">
// vue-router
import { type RouteRecordRaw } from "vue-router";
import type { CustomRouteRecordRaw } from "@/routers/interface/index";
import SubMenuItem from "./index.vue";
defineProps({
item: {
type: Object as () => RouteRecordRaw,
type: Object as () => CustomRouteRecordRaw,
required: true
},
basePath: {
Expand Down Expand Up @@ -35,7 +34,7 @@ defineProps({
v-for="(routeItem, index) in item.children"
:key="routeItem.path + index"
:item="routeItem"
:base-path="basePath + '/' + routeItem.path"
:base-path="routeItem.path"
/>
</el-sub-menu>
</template>
Expand Down
2 changes: 1 addition & 1 deletion src/layouts/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const routerInclude = computed<string[]>(() => {
<el-container>
<el-header><Navbar /></el-header>
<el-container>
<el-main style="height: calc(100vh - 60px)" class="bg-gray-100 dark-bg-black">
<el-main class="bg-gray-100 dark-bg-black h-[calc(100vh-60px)]">
<el-scrollbar>
<div class="dark:bg-[var(--theme-bg)]" p-5 bg-white rounded-lg>
<!-- 子路由 -->
Expand Down
90 changes: 54 additions & 36 deletions src/routers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,60 +3,66 @@ import NProgress from "@/configs/nprogress";
import { useGlobalStore } from "@/stores/modules/user";
import { useAppStore } from "@/stores/modules/app";

// * 导入所有router
const metaRouters = import.meta.glob("./modules/*.ts", { eager: true });

// * 处理路由表
export const routerArray: RouteRecordRaw[] = [];

Object.keys(metaRouters).forEach(item => {
Object.keys(metaRouters[item] as object).forEach(key => {
routerArray.push(...(metaRouters[item] as any)[key]);
});
});

/**
* Note: 路由配置项
*
* meta : {
requiresAuth: true // 如果设置为true,表示此页面需要用户认证
hidden: true // 当设置 true 的时候该路由不会在侧边栏出现 如401,login等页面,默认false
requiresAuth: true // 如果设置为true,表示此页面需要用户认证,默认false
title: 'title' // 设置该路由在侧边栏和面包屑中展示的名字
key: 'home' // 用于唯一标识此页面的键值
noKeepAlive: true // 如果设置为true,表明此页面组件不需要被缓存
icon: 'xianxingditu' // 置页面的图标为“xianxingditu”
noLayout: true // 如果设置为true,表示此页面不需要layout布局,默认为false
sort: 1 // 菜单排序,越大越靠后
}
*/

const routers: RouteRecordRaw[] = [
{
path: "/",
meta: {
hidden: true
},
redirect: "/layout",
children: []
},
{
path: "/layout",
name: "layout",
meta: {
hidden: true
},
component: () => import("@/layouts/index.vue"),
redirect: "/home",
children: [
{
path: "/home",
name: "Home",
component: () => import("@/views/home/index.vue"),
meta: {
requiresAuth: true,
title: "首页",
key: "home",
icon: "xianxingxiarilengyin"
}
}
]
children: []
},
...routerArray,
{
path: "/login",
name: "Login",
component: () => import("@/views/login/index.vue"),
meta: {
hidden: true,
requiresAuth: false,
title: "登录",
key: "login"
}
},
{
path: "/404",
name: "404",
component: () => import("@/views/error/404.vue"),
meta: {
hidden: true,
title: "404",
key: "err"
}
},
{
path: "/:catchAll(.*)",
redirect: "/404",
meta: {
hidden: true
}
}
];

Expand All @@ -76,22 +82,34 @@ router.beforeEach(async (to, from, next) => {
// 1.NProgress 开始
NProgress.start();

// 2.动态设置标题
// 2. 判断是否有 Token,没有重定向到 login
if (!globalStore.token && to.meta.requiresAuth) return next({ path: "/login", replace: true });

// 3. 添加路由
if (!appStore.menuList.length) {
await appStore.getMenuList();
appStore.addRouterList.forEach(route => {
if (!router.hasRoute(route.name as string)) {
if (route.meta?.noLayout) {
router.addRoute(route as RouteRecordRaw);
} else {
router.addRoute("layout", route as RouteRecordRaw);
}
}
});
return next({ path: to.redirectedFrom?.path, replace: true });
}

// 4.动态设置标题
const title = import.meta.env.VITE_APP_TITLE;
document.title = to.meta.title ? `${to.meta.title} - ${title}` : title;

// 3.判断是访问登陆页,有 Token 就在当前页面,没有 Token 重置路由并放行到登陆页
// 5.判断是访问登陆页,有 Token 就在当前页面,没有 Token 重置路由并放行到登陆页
if (to.path === "/login") {
if (globalStore.token) return next(from.fullPath);
return next();
}

// 4.判断是否有 Token,没有重定向到 login
if (!globalStore.token && to.meta.requiresAuth) return next({ path: "/login", replace: true });

// 5.设置面包屑
appStore.setCrumbsList(to.matched);

// 6.正常访问页面
next();
});
Expand Down
7 changes: 7 additions & 0 deletions src/routers/interface/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { RouteRecordRaw } from "vue-router";

// 扩展 RouteRecordRaw,允许 `component` 为字符串类型
export interface CustomRouteRecordRaw extends Omit<RouteRecordRaw, "component" | "children"> {
component?: string | RouteRecordRaw["component"];
children?: CustomRouteRecordRaw[];
}
91 changes: 46 additions & 45 deletions src/routers/modules/component.ts
Original file line number Diff line number Diff line change
@@ -1,128 +1,129 @@
import { RouteRecordRaw } from "vue-router";
import type { CustomRouteRecordRaw } from "../interface";

// 首页模块
const componentRouter: Array<RouteRecordRaw> = [
// 组件模块
const componentRouter: Array<CustomRouteRecordRaw> = [
{
path: "/component",
name: "Component",
component: () => import("@/layouts/index.vue"),
redirect: "/component/codeEditor",
meta: {
requiresAuth: true,
title: "组件模块",
key: "component",
icon: "xianxingdiqiu"
icon: "xianxingdiqiu",
sort: 3
},
redirect: "/component/codeEditor",
children: [
{
path: "codeEditor",
path: "/component/codeEditor",
name: "CodeEditor",
component: () => import("@/views/components/codeEditor/index.vue"),
component: "/components/codeEditor/index",
meta: {
requiresAuth: true,
title: "代码编辑器",
key: "codeEditor",
icon: "xianxingfeiji"
icon: "xianxingfeiji",
sort: 1
}
},
{
path: "wangEditor",
path: "/component/wangEditor",
name: "WangEditor",
component: () => import("@/views/components/wangEditor/index.vue"),
component: "/components/wangEditor/index",
meta: {
requiresAuth: true,
title: "富文本编辑器",
key: "wangEditor",
icon: "xianxinglvhangriji"
icon: "xianxinglvhangriji",
sort: 2
}
},
{
path: "mdEditor",
path: "/component/mdEditor",
name: "MdEditor",
component: () => import("@/views/components/mdEditor/index.vue"),
component: "/components/mdEditor/index",
meta: {
requiresAuth: true,
title: "Markdown编辑器",
key: "mdEditor",
icon: "xianxingdaoyu"
icon: "xianxingdaoyu",
sort: 3
}
},
{
path: "preview",
path: "/component/preview",
name: "Preview",
component: () => import("@/views/components/preview/index.vue"),
component: "/components/preview/index",
meta: {
requiresAuth: true,
title: "文件预览",
key: "preview",
icon: "xianxingfanchuan"
icon: "xianxingfanchuan",
sort: 4
}
},
{
path: "masonry",
path: "/component/masonry",
name: "Masonry",
component: () => import("@/views/components/masonry/index.vue"),
component: "/components/masonry/index",
meta: {
requiresAuth: true,
title: "瀑布流布局",
key: "masonry",
icon: "xianxingzijiayou"
icon: "xianxingzijiayou",
sort: 5
}
},
{
path: "qrCode",
path: "/component/qrCode",
name: "QrCode",
component: () => import("@/views/components/qrCode/index.vue"),
component: "/components/qrCode/index",
meta: {
requiresAuth: true,
title: "生成二维码",
key: "qrCode",
icon: "xianxingtianqiyubao"
icon: "xianxingtianqiyubao",
sort: 6
}
},
{
path: "fragment",
path: "/component/fragment",
name: "Fragment",
component: () => import("@/views/components/fragment/index.vue"),
component: "/components/fragment/index",
meta: {
requiresAuth: true,
title: "碎片化图片",
key: "fragment",
noKeepAlive: true,
icon: "xianxingditu"
icon: "xianxingditu",
sort: 7
}
},
{
path: "splitPanel",
path: "/component/splitPanel",
name: "SplitPanel",
component: () => import("@/views/components/splitPanel/index.vue"),
component: "/components/splitPanel/index",
meta: {
requiresAuth: true,
title: "分割面板",
key: "splitPanel",
icon: "xianxingxiarilengyin"
icon: "xianxingxiarilengyin",
sort: 8
}
},
{
path: "draggable",
path: "/component/draggable",
name: "Draggable",
component: () => import("@/views/components/draggable/index.vue"),
component: "/components/draggable/index",
meta: {
requiresAuth: true,
title: "拖动面板",
key: "draggable",
icon: "xianxingzijiayou"
icon: "xianxingzijiayou",
sort: 9
}
},
{
path: "jsonPretty",
path: "/component/jsonPretty",
name: "JsonPretty",
component: () => import("@/views/components/jsonPretty/index.vue"),
component: "/components/jsonPretty/index",
meta: {
requiresAuth: true,
title: "JSON美化",
key: "jsonPretty",
icon: "xianxingyoulun"
icon: "xianxingyoulun",
sort: 10
}
}
]
Expand Down
Loading

0 comments on commit eea8173

Please sign in to comment.