实现 vue 动态路由的测试,登录 user 和 admin 可以访问不同的菜单

router/index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import { createWebHashHistory, createRouter, RouteRecordRaw } from "vue-router"

// 静态路由
const routes: RouteRecordRaw[] = [
{
name: "home",
path: "/",
meta: { label: "首页" },
component: () => import("../pages/home.vue"),
},
{
name: "login",
path: "/login",
meta: { label: "登录" },
component: () => import("../pages/login.vue"),
},

{
name: "disboard",
path: "/disboard",
meta: { label: "工作台" },
component: () => import("../pages/disboard.vue"),
},
]

// 动态路由
const dynamicRoutes: RouteRecordRaw[] = [
{
name: "list",
path: "/list",
meta: { label: "列表", roles: ["admin", "user"] },
component: () => import("../pages/list.vue"),
},
{
name: "about",
path: "/about",
meta: { label: "关于", roles: ["admin", "user"] },
component: () => import("../pages/about.vue"),
},
{
name: "user",
path: "/user",
meta: { label: "用户", roles: ["admin", "user"] },
component: () => import("../pages/user.vue"),
children: [
{
name: "user-info",
path: "/user/info",
meta: { label: "用户info", roles: ["admin", "user"] },
component: () => import("../pages/user-info.vue"),
children: [
{
name: "user-info.a",
path: "/user/info/a",
meta: { label: "用户info.a", roles: ["admin", "user"] },
component: () => import("../pages/user-info-a.vue"),
},
{
name: "user-info.b",
path: "/user/info/b",
meta: { label: "用户info.b", roles: ["admin", "user"] },
component: () => import("../pages/user-info-b.vue"),
},
],
},
{
name: "user-orders",
path: "/user/orders",
meta: { label: "用户orders", roles: ["user"] },
component: () => import("../pages/user-orders.vue"),
},
],
},
]

// routes 扁平化处理
const loopRoutes = (routes: RouteRecordRaw[], role: string = "") => {
const result: RouteRecordRaw[] = []
const loopChildren = (children: RouteRecordRaw[] = [], parent: string = "") => {
children.forEach((item) => {
if (item.children) {
const { children, ...rest } = item
if (!rest.meta?.roles || (rest.meta?.roles as string[]).includes(role)) {
result.push(rest)
if (!router.hasRoute(item.name!)) {
if (parent) {
router.addRoute(parent, rest)
} else {
router.addRoute(rest)
}
}
}
loopChildren(children, rest.name as string)
} else {
// meta信息中没有roles或者meta信息中roles包含当前登录用户的role
if (!item.meta?.roles || (item.meta?.roles as string[]).includes(role)) {
result.push(item)
if (!router.hasRoute(item.name!)) {
if (parent) {
// 有父级
router.addRoute(parent, item)
} else {
router.addRoute(item)
}
}
}
}
})
}
loopChildren(dynamicRoutes)
console.log(result)
return [...routes, ...result].filter((item) => item.name !== "login")
}

const router = createRouter({
history: createWebHashHistory(),
routes,
})

export default router

export { routes, loopRoutes }
App.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<script setup lang="ts">
import { provide, ref, watch } from "vue"
import router, { routes } from "./router"
const showRoutes = ref(routes)
provide("showRoutes", showRoutes)
const logout = () => {
location.href = "/"
}
watch(showRoutes, () => {
console.log(router.getRoutes()) // 查看当前路由列表
})
</script>

<template>
<router-link v-for="route in showRoutes" :key="route.name" :to="{ name: route.name }">
【{{ route.meta?.label }}】
</router-link>
<button @click="logout">退出登录</button>
<hr />
<router-view></router-view>
</template>

<style scoped></style>
login.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<script lang="ts" setup>
import { inject, Ref } from "vue"
import { loopRoutes, routes } from "../router"

const showRoutes = inject("showRoutes") as unknown as Ref
const login = (userType: string) => {
showRoutes.value = loopRoutes(routes, userType)
}
</script>

<template>
login.vue
<button @click="login('user')">用户登录</button>
<button @click="login('admin')">admin登录</button>
</template>

<style scoped lang="scss"></style>